tango-python 1.7.0__tar.gz → 1.8.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 (206) hide show
  1. {tango_python-1.7.0 → tango_python-1.8.0}/CHANGELOG.md +13 -0
  2. {tango_python-1.7.0 → tango_python-1.8.0}/PKG-INFO +1 -1
  3. {tango_python-1.7.0 → tango_python-1.8.0}/docs/API_REFERENCE.md +90 -0
  4. {tango_python-1.7.0 → tango_python-1.8.0}/docs/WEBHOOKS.md +1 -0
  5. {tango_python-1.7.0 → tango_python-1.8.0}/pyproject.toml +1 -1
  6. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/check_filter_shape_conformance.py +1 -0
  7. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/check_shape_coverage.py +1 -0
  8. {tango_python-1.7.0 → tango_python-1.8.0}/tango/__init__.py +3 -1
  9. {tango_python-1.7.0 → tango_python-1.8.0}/tango/client.py +158 -7
  10. {tango_python-1.7.0 → tango_python-1.8.0}/tango/models.py +77 -0
  11. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/explicit_schemas.py +78 -0
  12. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/generated_overlay.py +34 -0
  13. tango_python-1.8.0/tests/test_federal_register.py +233 -0
  14. tango_python-1.8.0/tests/test_organization_scope.py +107 -0
  15. {tango_python-1.7.0 → tango_python-1.8.0}/.gitignore +0 -0
  16. {tango_python-1.7.0 → tango_python-1.8.0}/LICENSE +0 -0
  17. {tango_python-1.7.0 → tango_python-1.8.0}/README.md +0 -0
  18. {tango_python-1.7.0 → tango_python-1.8.0}/docs/CLIENT.md +0 -0
  19. {tango_python-1.7.0 → tango_python-1.8.0}/docs/DYNAMIC_MODELS.md +0 -0
  20. {tango_python-1.7.0 → tango_python-1.8.0}/docs/ERRORS.md +0 -0
  21. {tango_python-1.7.0 → tango_python-1.8.0}/docs/PAGINATION.md +0 -0
  22. {tango_python-1.7.0 → tango_python-1.8.0}/docs/SHAPES.md +0 -0
  23. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/README.md +0 -0
  24. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/fetch_api_schema.py +0 -0
  25. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/generate_schemas_from_api.py +0 -0
  26. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/generate_shape_overlay.py +0 -0
  27. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/pr_review.py +0 -0
  28. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/probe_shape_types.py +0 -0
  29. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/refresh_contract.py +0 -0
  30. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/smoke_api_parity.py +0 -0
  31. {tango_python-1.7.0 → tango_python-1.8.0}/scripts/test_production.py +0 -0
  32. {tango_python-1.7.0 → tango_python-1.8.0}/tango/exceptions.py +0 -0
  33. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/__init__.py +0 -0
  34. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/factory.py +0 -0
  35. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/generator.py +0 -0
  36. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/models.py +0 -0
  37. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/parser.py +0 -0
  38. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/schema.py +0 -0
  39. {tango_python-1.7.0 → tango_python-1.8.0}/tango/shapes/types.py +0 -0
  40. {tango_python-1.7.0 → tango_python-1.8.0}/tango/webhooks/__init__.py +0 -0
  41. {tango_python-1.7.0 → tango_python-1.8.0}/tango/webhooks/cli.py +0 -0
  42. {tango_python-1.7.0 → tango_python-1.8.0}/tango/webhooks/receiver.py +0 -0
  43. {tango_python-1.7.0 → tango_python-1.8.0}/tango/webhooks/signing.py +0 -0
  44. {tango_python-1.7.0 → tango_python-1.8.0}/tango/webhooks/simulate.py +0 -0
  45. {tango_python-1.7.0 → tango_python-1.8.0}/tests/__init__.py +0 -0
  46. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
  47. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
  48. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
  49. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
  50. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
  51. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
  52. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
  53. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
  54. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
  55. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
  56. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
  57. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
  58. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
  59. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
  60. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
  61. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
  62. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
  63. {tango_python-1.7.0 → tango_python-1.8.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
  64. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  65. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
  66. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
  67. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
  68. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
  69. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
  70. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
  71. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
  72. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
  73. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
  74. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
  75. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
  76. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
  77. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
  78. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
  79. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
  80. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
  81. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
  82. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
  83. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
  84. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
  85. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
  86. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
  87. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_budget_flows +0 -0
  88. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
  89. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
  90. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
  91. {tango_python-1.7.0 → tango_python-1.8.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
  92. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
  93. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  94. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  95. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
  96. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
  97. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
  98. {tango_python-1.7.0 → tango_python-1.8.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
  99. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
  100. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
  101. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
  102. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
  103. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
  104. {tango_python-1.7.0 → tango_python-1.8.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
  105. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
  106. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
  107. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
  108. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
  109. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
  110. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
  111. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
  112. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
  113. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
  114. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
  115. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
  116. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
  117. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
  118. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
  119. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
  120. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
  121. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
  122. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +0 -0
  123. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
  124. {tango_python-1.7.0 → tango_python-1.8.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
  125. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
  126. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
  127. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
  128. {tango_python-1.7.0 → tango_python-1.8.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
  129. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  130. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
  131. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
  132. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
  133. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
  134. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
  135. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
  136. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
  137. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
  138. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
  139. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
  140. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
  141. {tango_python-1.7.0 → tango_python-1.8.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
  142. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  143. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
  144. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
  145. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
  146. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
  147. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
  148. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
  149. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
  150. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[minimal-case_id,case_number,title,source_system,outcome,filed_date] +0 -0
  151. {tango_python-1.7.0 → tango_python-1.8.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
  152. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
  153. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
  154. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
  155. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  156. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
  157. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  158. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  159. {tango_python-1.7.0 → tango_python-1.8.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
  160. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  161. {tango_python-1.7.0 → tango_python-1.8.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
  162. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  163. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
  164. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +0 -0
  165. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
  166. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +0 -0
  167. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
  168. {tango_python-1.7.0 → tango_python-1.8.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +0 -0
  169. {tango_python-1.7.0 → tango_python-1.8.0}/tests/conftest.py +0 -0
  170. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/README.md +0 -0
  171. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/__init__.py +0 -0
  172. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/conftest.py +0 -0
  173. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_agencies_integration.py +0 -0
  174. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_contracts_integration.py +0 -0
  175. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_edge_cases_integration.py +0 -0
  176. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_entities_integration.py +0 -0
  177. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_forecasts_integration.py +0 -0
  178. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_grants_integration.py +0 -0
  179. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_itdashboard_integration.py +0 -0
  180. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_naics_integration.py +0 -0
  181. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_notices_integration.py +0 -0
  182. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_offices_integration.py +0 -0
  183. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_opportunities_integration.py +0 -0
  184. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_organizations_integration.py +0 -0
  185. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
  186. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_protests_integration.py +0 -0
  187. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_reference_data_integration.py +0 -0
  188. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_subawards_integration.py +0 -0
  189. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
  190. {tango_python-1.7.0 → tango_python-1.8.0}/tests/integration/validation.py +0 -0
  191. {tango_python-1.7.0 → tango_python-1.8.0}/tests/production/__init__.py +0 -0
  192. {tango_python-1.7.0 → tango_python-1.8.0}/tests/production/conftest.py +0 -0
  193. {tango_python-1.7.0 → tango_python-1.8.0}/tests/production/test_production_smoke.py +0 -0
  194. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_api_parity.py +0 -0
  195. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_client.py +0 -0
  196. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_contract_appeals.py +0 -0
  197. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_dibbs_exclusions_sbir.py +0 -0
  198. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_models.py +0 -0
  199. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_opportunity_attachments.py +0 -0
  200. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_shape_coverage_gate.py +0 -0
  201. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_shapes.py +0 -0
  202. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_sled.py +0 -0
  203. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_webhooks_cli.py +0 -0
  204. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_webhooks_receiver.py +0 -0
  205. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_webhooks_signing.py +0 -0
  206. {tango_python-1.7.0 → tango_python-1.8.0}/tests/test_webhooks_simulate.py +0 -0
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.8.0] - 2026-09-28
11
+
12
+ ### Added
13
+ - **Federal Register documents** (Tango API 5.3.0). Two methods over the new `/api/federal_register/` resource: `list_federal_register_documents()` and `get_federal_register_document()`, plus a `FederalRegisterDocument` schema and the `FEDERAL_REGISTER_MINIMAL` / `FEDERAL_REGISTER_COMPREHENSIVE` defaults. These are the rules, proposed rules, notices and presidential documents published in the Federal Register since 1994. All seventeen of the API's filters are explicit named parameters, including `comments_open`, `cfr_title` / `cfr_part`, `rin`, and both `agency` (a Tango organization, including its sub-agencies) and `fr_agency` (the Federal Register's own agency slug).
14
+
15
+ `get_federal_register_document()` takes the document's `uuid`, not its `document_number`, because a document number is not unique on its own: the Federal Register reused some before 2016. Look a document up by number with `list_federal_register_documents(document_number=...)`. `full_text` is available on the detail method only when named in `shape`, and neither default shape includes it, since it can run to several MB. `ordering="rank"` requires a non-empty `search`, matching the API.
16
+ - **`alerts.federal_register.match` is an alertable event type.** `create_webhook_alert(query_type="federal_register", ...)` works.
17
+ - **`agency` filter on `list_budget_accounts()`, `list_exclusions()` and `list_itdashboard_investments()`** (Tango API 5.3.0). It takes a Tango agency name, abbreviation, code or organization key (e.g. `EPA`) and matches the whole organization subtree, so a department includes its sub-agencies; OR several with `|`. On exclusions it scopes by the excluding agency. On IT Dashboard it is available at every plan.
18
+ - **The `organization` expand is accepted on budget accounts, exclusions, SBIR topics and SBIR solicitations**, along with the `organization_id` field on budget accounts and exclusions. `organization(...)` carries `agency_code`, `agency_name`, `department_code`, `department_name`, `office_code`, `office_name` and `organization_id`. The SDK rejected these shapes client-side before. The default shapes are unchanged.
19
+
20
+ ### Fixed
21
+ - **`list_itdashboard_investments()` documents the API's current filter tiers.** `agency_name` is available at every plan, not Business+ only.
22
+
10
23
  ## [1.7.0] - 2026-09-23
11
24
 
12
25
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tango-python
3
- Version: 1.7.0
3
+ Version: 1.8.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
@@ -23,6 +23,7 @@ Complete reference for all Tango Python SDK methods and functionality.
23
23
  - [GSA eLibrary Contracts](#gsa-elibrary-contracts)
24
24
  - [Protests](#protests)
25
25
  - [Contract Appeals](#contract-appeals)
26
+ - [Federal Register](#federal-register)
26
27
  - [Budget](#budget)
27
28
  - [Business Types](#business-types)
28
29
  - [NAICS](#naics)
@@ -1315,6 +1316,93 @@ appeal = client.get_contract_appeal(
1315
1316
 
1316
1317
  ---
1317
1318
 
1319
+ ## Federal Register
1320
+
1321
+ Federal Register documents — rules, proposed rules, notices and presidential documents published since 1994.
1322
+
1323
+ A document is identified by `uuid`. `document_number` is not unique on its own: the Federal Register reused some numbers before 2016, so `document_number=` can return more than one document.
1324
+
1325
+ ### list_federal_register_documents()
1326
+
1327
+ List documents with filtering and shaping.
1328
+
1329
+ ```python
1330
+ documents = client.list_federal_register_documents(
1331
+ page=1,
1332
+ limit=25,
1333
+ shape=ShapeConfig.FEDERAL_REGISTER_MINIMAL,
1334
+ # Filter parameters (all optional)
1335
+ search=None,
1336
+ document_number=None,
1337
+ type=None,
1338
+ agency=None,
1339
+ fr_agency=None,
1340
+ publication_date_after=None,
1341
+ publication_date_before=None,
1342
+ effective_on_after=None,
1343
+ effective_on_before=None,
1344
+ comments_close_on_after=None,
1345
+ comments_close_on_before=None,
1346
+ comments_open=None,
1347
+ cfr_title=None,
1348
+ cfr_part=None,
1349
+ significant=None,
1350
+ rin=None,
1351
+ executive_order_number=None,
1352
+ ordering=None,
1353
+ )
1354
+ ```
1355
+
1356
+ **Filter Parameters:**
1357
+ - `search` - Ranked full-text search over the title, abstract and action; wrap in double quotes for a phrase
1358
+ - `document_number` - Exact FR document number, e.g. `"2016-31922"`; OR several with `|`
1359
+ - `type` - `"Notice"`, `"Rule"`, `"Proposed Rule"`, `"Presidential Document"`, `"Correction"`, `"Sunshine Act Document"` or `"Uncategorized Document"` (case-insensitive); OR several with `|`. An unknown type is an error, not an empty page. Documents published before 2008 are mostly `Uncategorized Document`, so a type filter undercounts that era
1360
+ - `agency` - A Tango agency name, abbreviation, code or organization key, e.g. `"EPA"`. Matches a document when any agency it lists falls within that organization, so a department includes its sub-agencies; OR several with `|`
1361
+ - `fr_agency` - The Federal Register's own agency slug, e.g. `"environmental-protection-agency"`; OR several with `|`
1362
+ - `publication_date_after` / `publication_date_before` - Publication date range (`YYYY-MM-DD`)
1363
+ - `effective_on_after` / `effective_on_before` - Effective date range (`YYYY-MM-DD`)
1364
+ - `comments_close_on_after` / `comments_close_on_before` - Comment-deadline range (`YYYY-MM-DD`)
1365
+ - `comments_open` - `True` for documents whose comment period closes today or later, `False` for those already closed; documents with no comment deadline match neither
1366
+ - `cfr_title` - A CFR title number, e.g. `"40"`
1367
+ - `cfr_part` - A CFR part number, e.g. `"52"`. Requires `cfr_title`, and both must match the same CFR reference, so `cfr_title="40", cfr_part="52"` finds 40 CFR 52
1368
+ - `significant` - Significant under Executive Order 12866
1369
+ - `rin` - A Regulation Identifier Number, e.g. `"2060-AV16"`; OR several with `|`
1370
+ - `executive_order_number` - Exact executive order number
1371
+ - `ordering` - `publication_date` (the default, as `-publication_date`), `effective_on`, `comments_close_on`, `document_number` or `rank`. `rank` requires a non-empty `search`
1372
+
1373
+ **Returns:** [PaginatedResponse](#paginatedresponse) with document dictionaries
1374
+
1375
+ **Example:**
1376
+ ```python
1377
+ documents = client.list_federal_register_documents(
1378
+ type="Proposed Rule",
1379
+ comments_open=True,
1380
+ agency="EPA",
1381
+ limit=25,
1382
+ )
1383
+
1384
+ for doc in documents.results:
1385
+ print(f"{doc['document_number']} ({doc['publication_date']}): {doc['title']}")
1386
+ print(f" comments close {doc['comments_close_on']}")
1387
+ ```
1388
+
1389
+ ### get_federal_register_document()
1390
+
1391
+ Get a single document by `uuid`. To look one up by its document number, use `list_federal_register_documents(document_number=...)`.
1392
+
1393
+ ```python
1394
+ document = client.get_federal_register_document(
1395
+ "DOCUMENT_UUID",
1396
+ shape=ShapeConfig.FEDERAL_REGISTER_COMPREHENSIVE,
1397
+ )
1398
+ ```
1399
+
1400
+ **Notes:**
1401
+ - `agencies`, `cfr_references`, `dockets` and `topics` are the Federal Register's own structures, served as published. `agencies` carries Federal Register agency slugs, not Tango organization keys.
1402
+ - `full_text`, the document's plain text, is available on this method only and only when named in `shape` (e.g. `shape="uuid,title,full_text"`). It can run to several MB, so neither default shape includes it.
1403
+
1404
+ ---
1405
+
1318
1406
  ## State & Local (SLED)
1319
1407
 
1320
1408
  State, local and education procurement — solicitations that never appear on SAM.gov because they were never federal. **Beta**: coverage is partial and grows one jurisdiction at a time.
@@ -2311,6 +2399,8 @@ entity = client.get_entity("UEI_KEY", shape=ShapeConfig.ENTITIES_COMPREHENSIVE)
2311
2399
  | `PROTESTS_MINIMAL` | `list_protests` | case_id, case_number, title, source_system, outcome, filed_date |
2312
2400
  | `CONTRACT_APPEALS_MINIMAL` | `list_contract_appeals` | uuid, board, docket_numbers, decision_date, appellant, judge, decision_type, url |
2313
2401
  | `CONTRACT_APPEALS_COMPREHENSIVE` | `get_contract_appeal` | The list fields plus docket_raw, docket_source, decision_date_repaired, decision_type_raw, listing_year, first_listed_at, listed, text_status, text_char_count (omits `decision_text`, which needs an Enterprise plan) |
2402
+ | `FEDERAL_REGISTER_MINIMAL` | `list_federal_register_documents` | uuid, document_number, publication_date, type, subtype, title, abstract, action, agencies, cfr_references, citation, significant, comments_close_on, effective_on, html_url, pdf_url |
2403
+ | `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`) |
2314
2404
  | `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 |
2315
2405
  | `VEHICLE_ORDERS_MINIMAL` | `list_vehicle_orders` | key, piid, award_date, recipient(display_name,uei), total_contract_value, obligated |
2316
2406
  | `ITDASHBOARD_INVESTMENTS_MINIMAL` | `list_itdashboard_investments` | Minimal IT Dashboard investment fields |
@@ -89,6 +89,7 @@ tango webhooks list-event-types
89
89
  # alerts.dibbs_rfp.match DLA DIBBS RFP matched a saved alert
90
90
  # alerts.sled_opportunity.match State/local/education solicitation matched a saved alert
91
91
  # alerts.contract_appeal.match CBCA/ASBCA appeal decision matched a saved alert
92
+ # alerts.federal_register.match Federal Register document matched a saved alert
92
93
  ```
93
94
 
94
95
  This list is served by the API, so it is always current — the SDK does not hardcode
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tango-python"
7
- version = "1.7.0"
7
+ version = "1.8.0"
8
8
  description = "Python SDK for the Tango API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -78,6 +78,7 @@ RESOURCE_TO_METHOD: dict[str, str | None] = {
78
78
  "sled/forecasts": "list_sled_forecasts",
79
79
  "protests": "list_protests",
80
80
  "contract_appeals": "list_contract_appeals",
81
+ "federal_register": "list_federal_register_documents",
81
82
  "offices": "list_offices",
82
83
  "psc": "list_psc",
83
84
  "mas_sins": "list_mas_sins",
@@ -69,6 +69,7 @@ RESOURCE_TO_MODEL: dict[str, str] = {
69
69
  "budget_accounts": "BudgetAccount",
70
70
  "protests": "Protest",
71
71
  "contract_appeals": "ContractAppeal",
72
+ "federal_register": "FederalRegisterDocument",
72
73
  "offices": "Office",
73
74
  "assistance_listings": "AssistanceListing",
74
75
  "business_types": "BusinessType",
@@ -15,6 +15,7 @@ from .models import (
15
15
  DibbsRfp,
16
16
  DibbsRfq,
17
17
  Exclusion,
18
+ FederalRegisterDocument,
18
19
  GsaElibraryContract,
19
20
  ITDashboardInvestment,
20
21
  PaginatedResponse,
@@ -54,7 +55,7 @@ from .webhooks import (
54
55
  )
55
56
  from .webhooks.receiver import Delivery, WebhookReceiver
56
57
 
57
- __version__ = "1.7.0"
58
+ __version__ = "1.8.0"
58
59
  __all__ = [
59
60
  "TangoClient",
60
61
  "TangoAPIError",
@@ -67,6 +68,7 @@ __all__ = [
67
68
  "ResolveResult",
68
69
  "BudgetAccount",
69
70
  "ContractAppeal",
71
+ "FederalRegisterDocument",
70
72
  "DibbsAward",
71
73
  "DibbsRfp",
72
74
  "DibbsRfq",
@@ -30,6 +30,7 @@ from tango.models import (
30
30
  DibbsRfq,
31
31
  Entity,
32
32
  Exclusion,
33
+ FederalRegisterDocument,
33
34
  Forecast,
34
35
  Grant,
35
36
  GsaElibraryContract,
@@ -1601,18 +1602,18 @@ class TangoClient:
1601
1602
  cio_rating_max: int | None = None,
1602
1603
  performance_risk: bool | None = None,
1603
1604
  previous_uii: str | None = None,
1605
+ agency: str | None = None,
1604
1606
  ) -> PaginatedResponse:
1605
1607
  """List federal IT investments from the IT Dashboard (`/api/itdashboard/`).
1606
1608
 
1607
1609
  Filters are tier-gated by the API:
1608
1610
 
1609
- - **Free**: ``search`` (full-text across UII, title, description, agency,
1610
- bureau) and ``previous_uii`` — following a retired identifier forward to
1611
- whatever superseded it is recovery, not analysis, so it is ungated
1611
+ - **Free**: ``search`` (full-text across UII, title, description, agency, bureau), ``agency_name`` (text), ``agency`` and ``previous_uii`` — following a retired identifier forward to whatever superseded it is recovery, not analysis, so it is ungated
1612
1612
  - **Pro**: ``agency_code``, ``type_of_investment``,
1613
1613
  ``updated_time_after`` / ``updated_time_before``
1614
- - **Business+**: ``agency_name`` (text), ``cio_rating``,
1615
- ``cio_rating_max``, ``performance_risk``
1614
+ - **Business+**: ``cio_rating``, ``cio_rating_max``, ``performance_risk``
1615
+
1616
+ ``agency`` takes a Tango agency name, abbreviation, code or organization key, e.g. ``DOT``, and matches the whole organization subtree, so a department includes its sub-agencies. OR several with ``|``.
1616
1617
 
1617
1618
  Hitting a gated filter on a lower tier returns a 403 with upgrade info.
1618
1619
 
@@ -1632,6 +1633,7 @@ class TangoClient:
1632
1633
  params["flat_lists"] = "true"
1633
1634
  for k, val in (
1634
1635
  ("search", search),
1636
+ ("agency", agency),
1635
1637
  ("agency_code", agency_code),
1636
1638
  ("agency_name", agency_name),
1637
1639
  ("type_of_investment", type_of_investment),
@@ -2818,6 +2820,149 @@ class TangoClient:
2818
2820
  data = self._get(f"/api/contract_appeals/{appeal_id}/", params)
2819
2821
  return self._parse_response_with_shape(data, shape, ContractAppeal, flat, flat_lists)
2820
2822
 
2823
+ # ============================================================================
2824
+ # Federal Register documents
2825
+ # ============================================================================
2826
+
2827
+ def list_federal_register_documents(
2828
+ self,
2829
+ page: int = 1,
2830
+ limit: int = 25,
2831
+ shape: str | None = None,
2832
+ flat: bool = False,
2833
+ flat_lists: bool = False,
2834
+ search: str | None = None,
2835
+ document_number: str | None = None,
2836
+ type: str | None = None,
2837
+ agency: str | None = None,
2838
+ fr_agency: str | None = None,
2839
+ publication_date_after: str | None = None,
2840
+ publication_date_before: str | None = None,
2841
+ effective_on_after: str | None = None,
2842
+ effective_on_before: str | None = None,
2843
+ comments_close_on_after: str | None = None,
2844
+ comments_close_on_before: str | None = None,
2845
+ comments_open: bool | None = None,
2846
+ cfr_title: str | None = None,
2847
+ cfr_part: str | None = None,
2848
+ significant: bool | None = None,
2849
+ rin: str | None = None,
2850
+ executive_order_number: str | None = None,
2851
+ ordering: str | None = None,
2852
+ ) -> PaginatedResponse:
2853
+ """
2854
+ List Federal Register documents.
2855
+
2856
+ Rules, proposed rules, notices and presidential documents published in the Federal Register since 1994.
2857
+
2858
+ Args:
2859
+ page: Page number
2860
+ limit: Results per page (max 100)
2861
+ shape: Response shape string (defaults to minimal shape)
2862
+ flat: If True, flatten nested objects in shaped response
2863
+ flat_lists: If True, flatten arrays using indexed keys
2864
+ search: Ranked full-text search over the title, abstract and action. Wrap in double quotes for a phrase
2865
+ document_number: Exact FR document number, e.g. ``2016-31922``. OR several with ``|``. A number the Federal Register reused before 2016 matches every document that carries it
2866
+ type: ``Notice``, ``Rule``, ``Proposed Rule``, ``Presidential Document``, ``Correction``, ``Sunshine Act Document`` or ``Uncategorized Document`` (case-insensitive). OR several with ``|``. Documents published before 2008 are mostly ``Uncategorized Document``
2867
+ agency: A Tango agency name, abbreviation, code or organization key, e.g. ``EPA``. Matches a document when any agency it lists falls within that organization, so a department includes its sub-agencies. OR several with ``|``
2868
+ fr_agency: The Federal Register's own agency slug, e.g. ``environmental-protection-agency``. OR several with ``|``
2869
+ publication_date_after: Published on or after (YYYY-MM-DD)
2870
+ publication_date_before: Published on or before (YYYY-MM-DD)
2871
+ effective_on_after: Effective on or after (YYYY-MM-DD)
2872
+ effective_on_before: Effective on or before (YYYY-MM-DD)
2873
+ comments_close_on_after: Comment period closes on or after (YYYY-MM-DD)
2874
+ comments_close_on_before: Comment period closes on or before (YYYY-MM-DD)
2875
+ comments_open: ``True`` for documents whose comment period closes today or later, ``False`` for those already closed. Documents with no comment deadline match neither
2876
+ cfr_title: A CFR title number, e.g. ``40``
2877
+ cfr_part: A CFR part number, e.g. ``52``. Requires ``cfr_title``, and both must match the same CFR reference
2878
+ significant: Significant under Executive Order 12866
2879
+ rin: A Regulation Identifier Number, e.g. ``2060-AV16``. OR several with ``|``
2880
+ executive_order_number: Exact executive order number
2881
+ ordering: Sort field — ``publication_date`` (the default, as ``-publication_date``), ``effective_on``, ``comments_close_on``, ``document_number`` or ``rank``. ``rank`` requires a non-empty ``search``
2882
+ """
2883
+ params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
2884
+
2885
+ if shape is None:
2886
+ shape = ShapeConfig.FEDERAL_REGISTER_MINIMAL
2887
+ if shape:
2888
+ params["shape"] = shape
2889
+ if flat:
2890
+ params["flat"] = "true"
2891
+ if flat_lists:
2892
+ params["flat_lists"] = "true"
2893
+
2894
+ for key, val in (
2895
+ ("search", search),
2896
+ ("document_number", document_number),
2897
+ ("type", type),
2898
+ ("agency", agency),
2899
+ ("fr_agency", fr_agency),
2900
+ ("publication_date_after", publication_date_after),
2901
+ ("publication_date_before", publication_date_before),
2902
+ ("effective_on_after", effective_on_after),
2903
+ ("effective_on_before", effective_on_before),
2904
+ ("comments_close_on_after", comments_close_on_after),
2905
+ ("comments_close_on_before", comments_close_on_before),
2906
+ ("comments_open", comments_open),
2907
+ ("cfr_title", cfr_title),
2908
+ ("cfr_part", cfr_part),
2909
+ ("significant", significant),
2910
+ ("rin", rin),
2911
+ ("executive_order_number", executive_order_number),
2912
+ ("ordering", ordering),
2913
+ ):
2914
+ if val is not None:
2915
+ params[key] = val
2916
+
2917
+ data = self._get("/api/federal_register/", params)
2918
+
2919
+ results = [
2920
+ self._parse_response_with_shape(item, shape, FederalRegisterDocument, flat, flat_lists)
2921
+ for item in data["results"]
2922
+ ]
2923
+
2924
+ return PaginatedResponse(
2925
+ count=data["count"],
2926
+ next=data.get("next"),
2927
+ previous=data.get("previous"),
2928
+ results=results,
2929
+ meta=data.get("meta"),
2930
+ )
2931
+
2932
+ def get_federal_register_document(
2933
+ self,
2934
+ document_uuid: str,
2935
+ shape: str | None = None,
2936
+ flat: bool = False,
2937
+ flat_lists: bool = False,
2938
+ ) -> Any:
2939
+ """
2940
+ Get a single Federal Register document by uuid.
2941
+
2942
+ The detail route takes the document's ``uuid``, not its ``document_number``, which is not unique on its own.
2943
+ To look a document up by number, use ``list_federal_register_documents(document_number=...)``.
2944
+
2945
+ Args:
2946
+ document_uuid: Document UUID
2947
+ shape: Response shape string (defaults to the comprehensive shape). Name ``full_text`` here to get the document's plain text, which can run to several MB
2948
+ flat: If True, flatten nested objects in shaped response
2949
+ flat_lists: If True, flatten arrays using indexed keys
2950
+ """
2951
+ params: dict[str, Any] = {}
2952
+ if shape is None:
2953
+ shape = ShapeConfig.FEDERAL_REGISTER_COMPREHENSIVE
2954
+ if shape:
2955
+ params["shape"] = shape
2956
+ if flat:
2957
+ params["flat"] = "true"
2958
+ if flat_lists:
2959
+ params["flat_lists"] = "true"
2960
+
2961
+ data = self._get(f"/api/federal_register/{document_uuid}/", params)
2962
+ return self._parse_response_with_shape(
2963
+ data, shape, FederalRegisterDocument, flat, flat_lists
2964
+ )
2965
+
2821
2966
  # ============================================================================
2822
2967
  # DLA DIBBS (RFQs, RFPs, awards)
2823
2968
  # ============================================================================
@@ -3242,6 +3387,7 @@ class TangoClient:
3242
3387
  update_date_before: str | None = None,
3243
3388
  search: str | None = None,
3244
3389
  ordering: str | None = None,
3390
+ agency: str | None = None,
3245
3391
  ) -> PaginatedResponse:
3246
3392
  """
3247
3393
  List SAM.gov exclusion (debarment) records.
@@ -3261,6 +3407,7 @@ class TangoClient:
3261
3407
  classification_type: Filter by classification (Firm, Individual, Vessel, ...)
3262
3408
  exclusion_type: Filter by exclusion type
3263
3409
  exclusion_program: Filter by exclusion program
3410
+ agency: Excluding agency as a Tango organization. A Tango agency name, abbreviation, code or organization key, e.g. ``EPA``. Matches the whole organization subtree, so a department includes its sub-agencies. OR several with ``|``
3264
3411
  excluding_agency_code: Filter by excluding agency code
3265
3412
  excluding_agency_name: Filter by excluding agency name
3266
3413
  active: True returns only records currently in effect.
@@ -3296,6 +3443,7 @@ class TangoClient:
3296
3443
  ("classification_type", classification_type),
3297
3444
  ("exclusion_type", exclusion_type),
3298
3445
  ("exclusion_program", exclusion_program),
3446
+ ("agency", agency),
3299
3447
  ("excluding_agency_code", excluding_agency_code),
3300
3448
  ("excluding_agency_name", excluding_agency_name),
3301
3449
  ("active", active),
@@ -4153,6 +4301,7 @@ class TangoClient:
4153
4301
  actual_vs_requested_contract_capped_lte: float | None = None,
4154
4302
  search: str | None = None,
4155
4303
  ordering: str | None = None,
4304
+ agency: str | None = None,
4156
4305
  ) -> PaginatedResponse:
4157
4306
  """List budget accounts (`/api/budget/accounts/`).
4158
4307
 
@@ -4170,6 +4319,7 @@ class TangoClient:
4170
4319
  fiscal_year: Fiscal year (exact).
4171
4320
  fiscal_year_gte: Fiscal year >=.
4172
4321
  fiscal_year_lte: Fiscal year <=.
4322
+ agency: A Tango agency name, abbreviation, code or organization key, e.g. ``EPA``. Matches the whole organization subtree, so a department includes its sub-agencies. OR several with ``|``.
4173
4323
  agency_code: Agency code (exact).
4174
4324
  bureau_name: Bureau name (exact).
4175
4325
  account_title: Account title (icontains).
@@ -4234,6 +4384,7 @@ class TangoClient:
4234
4384
  ("fiscal_year", fiscal_year),
4235
4385
  ("fiscal_year__gte", fiscal_year_gte),
4236
4386
  ("fiscal_year__lte", fiscal_year_lte),
4387
+ ("agency", agency),
4237
4388
  ("agency_code", agency_code),
4238
4389
  ("bureau_name", bureau_name),
4239
4390
  ("account_title__icontains", account_title),
@@ -4870,8 +5021,8 @@ class TangoClient:
4870
5021
  name: Human-readable name for this alert.
4871
5022
  query_type: One of ``opportunity``, ``contract``, ``idv``, ``ota``,
4872
5023
  ``otidv``, ``entity``, ``grant``, ``forecast``, ``exclusion``,
4873
- ``dibbs_rfq``, ``dibbs_rfp``, ``sled_opportunity`` or
4874
- ``contract_appeal``. :meth:`list_webhook_event_types` is the
5024
+ ``dibbs_rfq``, ``dibbs_rfp``, ``sled_opportunity``,
5025
+ ``contract_appeal`` or ``federal_register``. :meth:`list_webhook_event_types` is the
4875
5026
  current authority — this list can only go stale.
4876
5027
  filters: Dict of query parameters that the alert matches against
4877
5028
  (e.g. ``{"naics": "541330", "set_aside": "SBA"}``).
@@ -1213,6 +1213,65 @@ class ContractAppeal:
1213
1213
  decision_text: str | None = None
1214
1214
 
1215
1215
 
1216
+ class FederalRegisterDocument:
1217
+ """Schema definition for FederalRegisterDocument (not used for instances).
1218
+
1219
+ Federal Register documents at ``/api/federal_register/``: rules, proposed rules, notices and presidential documents published since 1994.
1220
+ Shape-on-demand, so every field is optional.
1221
+
1222
+ A document is identified by ``uuid``.
1223
+ ``document_number`` alone is not unique: the Federal Register reused some numbers before 2016, so ``document_number=`` can match more than one document.
1224
+
1225
+ ``agencies``, ``cfr_references``, ``dockets`` and ``topics`` are the Federal Register's own structures, served as published.
1226
+ ``full_text`` can run to several MB, so it is available on the detail endpoint only and only when named in ``shape``.
1227
+ """
1228
+
1229
+ uuid: str | None = None
1230
+ document_number: str | None = None
1231
+ publication_date: date | None = None
1232
+ citation: str | None = None
1233
+ title: str | None = None
1234
+ type: str | None = None
1235
+ subtype: str | None = None
1236
+ abstract: str | None = None
1237
+ action: str | None = None
1238
+ dates: str | None = None
1239
+ comments_close_on: date | None = None
1240
+ effective_on: date | None = None
1241
+ signing_date: date | None = None
1242
+ start_page: int | None = None
1243
+ end_page: int | None = None
1244
+ page_length: int | None = None
1245
+ volume: int | None = None
1246
+ significant: bool | None = None
1247
+ toc_doc: str | None = None
1248
+ toc_subject: str | None = None
1249
+ correction_of: str | None = None
1250
+ corrections: list[str] | None = None
1251
+ disposition_notes: str | None = None
1252
+ executive_order_notes: str | None = None
1253
+ executive_order_number: str | None = None
1254
+ presidential_document_number: str | None = None
1255
+ proclamation_number: str | None = None
1256
+ not_received_for_publication: str | None = None
1257
+ comment_url: str | None = None
1258
+ regulations_dot_gov_url: str | None = None
1259
+ html_url: str | None = None
1260
+ body_html_url: str | None = None
1261
+ pdf_url: str | None = None
1262
+ public_inspection_pdf_url: str | None = None
1263
+ raw_text_url: str | None = None
1264
+ agencies: list[dict[str, Any]] | None = None
1265
+ cfr_references: list[dict[str, Any]] | None = None
1266
+ dockets: list[dict[str, Any]] | None = None
1267
+ docket_ids: list[str] | None = None
1268
+ topics: list[str] | None = None
1269
+ regulation_id_numbers: list[str] | None = None
1270
+ regulation_id_number_info: dict[str, Any] | None = None
1271
+ regulations_dot_gov_info: dict[str, Any] | None = None
1272
+ full_text: str | None = None
1273
+
1274
+
1216
1275
  @dataclass
1217
1276
  class PaginatedResponse[T]:
1218
1277
  """Paginated API response
@@ -1394,6 +1453,24 @@ class ShapeConfig:
1394
1453
  "listing_year,first_listed_at,listed,text_status,text_char_count"
1395
1454
  )
1396
1455
 
1456
+ # Default for list_federal_register_documents(). Mirrors the API's own list default.
1457
+ FEDERAL_REGISTER_MINIMAL: Final = (
1458
+ "uuid,document_number,publication_date,type,subtype,title,abstract,action,"
1459
+ "agencies,cfr_references,citation,significant,comments_close_on,effective_on,"
1460
+ "html_url,pdf_url"
1461
+ )
1462
+
1463
+ # Default for get_federal_register_document(). Mirrors the API's own retrieve default.
1464
+ # `full_text` is left out on purpose: it can run to several MB.
1465
+ FEDERAL_REGISTER_COMPREHENSIVE: Final = (
1466
+ "uuid,document_number,publication_date,type,subtype,title,abstract,action,"
1467
+ "agencies,cfr_references,citation,significant,comments_close_on,effective_on,"
1468
+ "html_url,pdf_url,dates,signing_date,start_page,end_page,volume,docket_ids,"
1469
+ "dockets,regulation_id_numbers,topics,correction_of,corrections,"
1470
+ "executive_order_number,presidential_document_number,proclamation_number,"
1471
+ "comment_url,regulations_dot_gov_url,raw_text_url,body_html_url"
1472
+ )
1473
+
1397
1474
  # Default for list_dibbs_rfqs()
1398
1475
  DIBBS_RFQS_MINIMAL: Final = (
1399
1476
  "uuid,solicitation,nsn,part_number,nomenclature,quantity,issue_date,return_by_date,is_open"
@@ -800,6 +800,83 @@ CONTRACT_APPEAL_SCHEMA: dict[str, FieldSchema] = {
800
800
  }
801
801
 
802
802
 
803
+ FEDERAL_REGISTER_DOCUMENT_SCHEMA: dict[str, FieldSchema] = {
804
+ "uuid": FieldSchema(name="uuid", type=str, is_optional=True, is_list=False),
805
+ "document_number": FieldSchema(
806
+ name="document_number", type=str, is_optional=True, is_list=False
807
+ ),
808
+ "publication_date": FieldSchema(
809
+ name="publication_date", type=date, is_optional=True, is_list=False
810
+ ),
811
+ "citation": FieldSchema(name="citation", type=str, is_optional=True, is_list=False),
812
+ "title": FieldSchema(name="title", type=str, is_optional=True, is_list=False),
813
+ "type": FieldSchema(name="type", type=str, is_optional=True, is_list=False),
814
+ "subtype": FieldSchema(name="subtype", type=str, is_optional=True, is_list=False),
815
+ "abstract": FieldSchema(name="abstract", type=str, is_optional=True, is_list=False),
816
+ "action": FieldSchema(name="action", type=str, is_optional=True, is_list=False),
817
+ "dates": FieldSchema(name="dates", type=str, is_optional=True, is_list=False),
818
+ "comments_close_on": FieldSchema(
819
+ name="comments_close_on", type=date, is_optional=True, is_list=False
820
+ ),
821
+ "effective_on": FieldSchema(name="effective_on", type=date, is_optional=True, is_list=False),
822
+ "signing_date": FieldSchema(name="signing_date", type=date, is_optional=True, is_list=False),
823
+ "start_page": FieldSchema(name="start_page", type=int, is_optional=True, is_list=False),
824
+ "end_page": FieldSchema(name="end_page", type=int, is_optional=True, is_list=False),
825
+ "page_length": FieldSchema(name="page_length", type=int, is_optional=True, is_list=False),
826
+ "volume": FieldSchema(name="volume", type=int, is_optional=True, is_list=False),
827
+ "significant": FieldSchema(name="significant", type=bool, is_optional=True, is_list=False),
828
+ "toc_doc": FieldSchema(name="toc_doc", type=str, is_optional=True, is_list=False),
829
+ "toc_subject": FieldSchema(name="toc_subject", type=str, is_optional=True, is_list=False),
830
+ "correction_of": FieldSchema(name="correction_of", type=str, is_optional=True, is_list=False),
831
+ "corrections": FieldSchema(name="corrections", type=str, is_optional=True, is_list=True),
832
+ "disposition_notes": FieldSchema(
833
+ name="disposition_notes", type=str, is_optional=True, is_list=False
834
+ ),
835
+ "executive_order_notes": FieldSchema(
836
+ name="executive_order_notes", type=str, is_optional=True, is_list=False
837
+ ),
838
+ "executive_order_number": FieldSchema(
839
+ name="executive_order_number", type=str, is_optional=True, is_list=False
840
+ ),
841
+ "presidential_document_number": FieldSchema(
842
+ name="presidential_document_number", type=str, is_optional=True, is_list=False
843
+ ),
844
+ "proclamation_number": FieldSchema(
845
+ name="proclamation_number", type=str, is_optional=True, is_list=False
846
+ ),
847
+ "not_received_for_publication": FieldSchema(
848
+ name="not_received_for_publication", type=str, is_optional=True, is_list=False
849
+ ),
850
+ "comment_url": FieldSchema(name="comment_url", type=str, is_optional=True, is_list=False),
851
+ "regulations_dot_gov_url": FieldSchema(
852
+ name="regulations_dot_gov_url", type=str, is_optional=True, is_list=False
853
+ ),
854
+ "html_url": FieldSchema(name="html_url", type=str, is_optional=True, is_list=False),
855
+ "body_html_url": FieldSchema(name="body_html_url", type=str, is_optional=True, is_list=False),
856
+ "pdf_url": FieldSchema(name="pdf_url", type=str, is_optional=True, is_list=False),
857
+ "public_inspection_pdf_url": FieldSchema(
858
+ name="public_inspection_pdf_url", type=str, is_optional=True, is_list=False
859
+ ),
860
+ "raw_text_url": FieldSchema(name="raw_text_url", type=str, is_optional=True, is_list=False),
861
+ "agencies": FieldSchema(name="agencies", type=dict, is_optional=True, is_list=True),
862
+ "cfr_references": FieldSchema(name="cfr_references", type=dict, is_optional=True, is_list=True),
863
+ "dockets": FieldSchema(name="dockets", type=dict, is_optional=True, is_list=True),
864
+ "docket_ids": FieldSchema(name="docket_ids", type=str, is_optional=True, is_list=True),
865
+ "topics": FieldSchema(name="topics", type=str, is_optional=True, is_list=True),
866
+ "regulation_id_numbers": FieldSchema(
867
+ name="regulation_id_numbers", type=str, is_optional=True, is_list=True
868
+ ),
869
+ "regulation_id_number_info": FieldSchema(
870
+ name="regulation_id_number_info", type=dict, is_optional=True, is_list=False
871
+ ),
872
+ "regulations_dot_gov_info": FieldSchema(
873
+ name="regulations_dot_gov_info", type=dict, is_optional=True, is_list=False
874
+ ),
875
+ # Detail endpoint only, and only when named in the shape.
876
+ "full_text": FieldSchema(name="full_text", type=str, is_optional=True, is_list=False),
877
+ }
878
+
879
+
803
880
  AGENCY_SCHEMA: dict[str, FieldSchema] = {
804
881
  "abbreviation": FieldSchema(name="abbreviation", type=str, is_optional=True, is_list=False),
805
882
  "code": FieldSchema(name="code", type=str, is_optional=False, is_list=False),
@@ -1545,6 +1622,7 @@ EXPLICIT_SCHEMAS: dict[str, dict[str, FieldSchema]] = {
1545
1622
  "Protest": PROTEST_SCHEMA,
1546
1623
  "ProtestDocket": PROTEST_DOCKET_SCHEMA,
1547
1624
  "ContractAppeal": CONTRACT_APPEAL_SCHEMA,
1625
+ "FederalRegisterDocument": FEDERAL_REGISTER_DOCUMENT_SCHEMA,
1548
1626
  "Agency": AGENCY_SCHEMA,
1549
1627
  "Grant": GRANT_SCHEMA,
1550
1628
  # Vehicles (Awards)