tango-python 0.4.3__tar.gz → 0.5.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 (182) hide show
  1. {tango_python-0.4.3 → tango_python-0.5.0}/CHANGELOG.md +12 -0
  2. {tango_python-0.4.3 → tango_python-0.5.0}/PKG-INFO +1 -1
  3. {tango_python-0.4.3 → tango_python-0.5.0}/pyproject.toml +1 -1
  4. {tango_python-0.4.3 → tango_python-0.5.0}/scripts/check_filter_shape_conformance.py +12 -0
  5. {tango_python-0.4.3 → tango_python-0.5.0}/tango/__init__.py +3 -1
  6. {tango_python-0.4.3 → tango_python-0.5.0}/tango/client.py +122 -0
  7. {tango_python-0.4.3 → tango_python-0.5.0}/tango/models.py +54 -0
  8. {tango_python-0.4.3 → tango_python-0.5.0}/tango/shapes/explicit_schemas.py +63 -0
  9. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +95 -0
  10. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +93 -0
  11. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +94 -0
  12. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +93 -0
  13. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +94 -0
  14. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +94 -0
  15. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +93 -0
  16. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +88 -0
  17. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +314 -0
  18. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +364 -0
  19. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +182 -0
  20. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +164 -0
  21. tango_python-0.5.0/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +184 -0
  22. tango_python-0.5.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] +184 -0
  23. tango_python-0.5.0/tests/integration/test_itdashboard_integration.py +302 -0
  24. {tango_python-0.4.3 → tango_python-0.5.0}/tests/test_client.py +143 -0
  25. {tango_python-0.4.3 → tango_python-0.5.0}/uv.lock +1 -1
  26. {tango_python-0.4.3 → tango_python-0.5.0}/.env.example +0 -0
  27. {tango_python-0.4.3 → tango_python-0.5.0}/.github/workflows/lint.yml +0 -0
  28. {tango_python-0.4.3 → tango_python-0.5.0}/.github/workflows/publish.yml +0 -0
  29. {tango_python-0.4.3 → tango_python-0.5.0}/.github/workflows/test.yml +0 -0
  30. {tango_python-0.4.3 → tango_python-0.5.0}/.gitignore +0 -0
  31. {tango_python-0.4.3 → tango_python-0.5.0}/LICENSE +0 -0
  32. {tango_python-0.4.3 → tango_python-0.5.0}/README.md +0 -0
  33. {tango_python-0.4.3 → tango_python-0.5.0}/ROADMAP.md +0 -0
  34. {tango_python-0.4.3 → tango_python-0.5.0}/docs/API_REFERENCE.md +0 -0
  35. {tango_python-0.4.3 → tango_python-0.5.0}/docs/DEVELOPERS.md +0 -0
  36. {tango_python-0.4.3 → tango_python-0.5.0}/docs/SHAPES.md +0 -0
  37. {tango_python-0.4.3 → tango_python-0.5.0}/docs/quick_start.ipynb +0 -0
  38. {tango_python-0.4.3 → tango_python-0.5.0}/scripts/README.md +0 -0
  39. {tango_python-0.4.3 → tango_python-0.5.0}/scripts/fetch_api_schema.py +0 -0
  40. {tango_python-0.4.3 → tango_python-0.5.0}/scripts/generate_schemas_from_api.py +0 -0
  41. {tango_python-0.4.3 → tango_python-0.5.0}/scripts/pr_review.py +0 -0
  42. {tango_python-0.4.3 → tango_python-0.5.0}/scripts/test_production.py +0 -0
  43. {tango_python-0.4.3 → tango_python-0.5.0}/tango/exceptions.py +0 -0
  44. {tango_python-0.4.3 → tango_python-0.5.0}/tango/shapes/__init__.py +0 -0
  45. {tango_python-0.4.3 → tango_python-0.5.0}/tango/shapes/factory.py +0 -0
  46. {tango_python-0.4.3 → tango_python-0.5.0}/tango/shapes/generator.py +0 -0
  47. {tango_python-0.4.3 → tango_python-0.5.0}/tango/shapes/models.py +0 -0
  48. {tango_python-0.4.3 → tango_python-0.5.0}/tango/shapes/parser.py +0 -0
  49. {tango_python-0.4.3 → tango_python-0.5.0}/tango/shapes/schema.py +0 -0
  50. {tango_python-0.4.3 → tango_python-0.5.0}/tango/shapes/types.py +0 -0
  51. {tango_python-0.4.3 → tango_python-0.5.0}/tests/__init__.py +0 -0
  52. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
  53. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
  54. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
  55. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
  56. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
  57. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
  58. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
  59. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
  60. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
  61. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
  62. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
  63. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
  64. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
  65. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
  66. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
  67. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
  68. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
  69. {tango_python-0.4.3 → tango_python-0.5.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
  70. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  71. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
  72. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
  73. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
  74. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
  75. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
  76. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
  77. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
  78. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
  79. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
  80. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
  81. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
  82. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
  83. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
  84. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
  85. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
  86. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
  87. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
  88. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
  89. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
  90. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
  91. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
  92. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
  93. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
  94. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
  95. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
  96. {tango_python-0.4.3 → tango_python-0.5.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
  97. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
  98. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  99. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  100. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
  101. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
  102. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
  103. {tango_python-0.4.3 → tango_python-0.5.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
  104. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
  105. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
  106. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
  107. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
  108. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
  109. {tango_python-0.4.3 → tango_python-0.5.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
  110. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
  111. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
  112. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
  113. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
  114. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
  115. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
  116. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
  117. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
  118. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
  119. {tango_python-0.4.3 → tango_python-0.5.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
  120. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  121. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
  122. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
  123. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
  124. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
  125. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
  126. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
  127. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
  128. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
  129. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
  130. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
  131. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
  132. {tango_python-0.4.3 → tango_python-0.5.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
  133. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  134. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
  135. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
  136. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
  137. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
  138. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
  139. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
  140. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
  141. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[minimal-case_id,case_number,title,source_system,outcome,filed_date] +0 -0
  142. {tango_python-0.4.3 → tango_python-0.5.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
  143. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
  144. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
  145. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
  146. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  147. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
  148. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  149. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  150. {tango_python-0.4.3 → tango_python-0.5.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
  151. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  152. {tango_python-0.4.3 → tango_python-0.5.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
  153. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  154. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
  155. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
  156. {tango_python-0.4.3 → tango_python-0.5.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
  157. {tango_python-0.4.3 → tango_python-0.5.0}/tests/conftest.py +0 -0
  158. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/README.md +0 -0
  159. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/__init__.py +0 -0
  160. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/conftest.py +0 -0
  161. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_agencies_integration.py +0 -0
  162. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_contracts_integration.py +0 -0
  163. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_edge_cases_integration.py +0 -0
  164. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_entities_integration.py +0 -0
  165. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_forecasts_integration.py +0 -0
  166. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_grants_integration.py +0 -0
  167. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_naics_integration.py +0 -0
  168. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_notices_integration.py +0 -0
  169. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_offices_integration.py +0 -0
  170. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_opportunities_integration.py +0 -0
  171. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_organizations_integration.py +0 -0
  172. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
  173. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_protests_integration.py +0 -0
  174. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_reference_data_integration.py +0 -0
  175. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_subawards_integration.py +0 -0
  176. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
  177. {tango_python-0.4.3 → tango_python-0.5.0}/tests/integration/validation.py +0 -0
  178. {tango_python-0.4.3 → tango_python-0.5.0}/tests/production/__init__.py +0 -0
  179. {tango_python-0.4.3 → tango_python-0.5.0}/tests/production/conftest.py +0 -0
  180. {tango_python-0.4.3 → tango_python-0.5.0}/tests/production/test_production_smoke.py +0 -0
  181. {tango_python-0.4.3 → tango_python-0.5.0}/tests/test_models.py +0 -0
  182. {tango_python-0.4.3 → tango_python-0.5.0}/tests/test_shapes.py +0 -0
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.0] - 2026-04-08
11
+
12
+ ### Added
13
+ - IT Dashboard investments: `list_itdashboard_investments`, `get_itdashboard_investment` (`/api/itdashboard/`) with shaping and filter params (`search`, `agency_code`, `agency_name`, `type_of_investment`, `updated_time_after`, `updated_time_before`, `cio_rating`, `cio_rating_max`, `performance_risk`). Tier-gated by the API: free tier gets `search`, pro adds structured filters, business+ adds CIO/performance analytics. New `ITDashboardInvestment` model and `ShapeConfig.ITDASHBOARD_INVESTMENTS_MINIMAL` / `ITDASHBOARD_INVESTMENTS_COMPREHENSIVE` defaults.
14
+
15
+ ## [0.4.4] - 2026-03-25
16
+
17
+ ### Added
18
+ - `parent_piid` filter parameter on `list_contracts` for filtering orders under a specific parent IDV PIID.
19
+ - `user_agent` and `extra_headers` parameters on `TangoClient` for custom request headers.
20
+ - `TangoClient.last_response_headers` property for accessing full HTTP headers from the most recent API response.
21
+
10
22
  ## [0.4.3] - 2026-03-21
11
23
 
12
24
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tango-python
3
- Version: 0.4.3
3
+ Version: 0.5.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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tango-python"
7
- version = "0.4.3"
7
+ version = "0.5.0"
8
8
  description = "Python SDK for the Tango API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -50,6 +50,7 @@ RESOURCE_TO_METHOD: dict[str, str] = {
50
50
  "agencies": "list_agencies",
51
51
  "naics": "list_naics",
52
52
  "gsa_elibrary_contracts": "list_gsa_elibrary_contracts",
53
+ "itdashboard": "list_itdashboard_investments",
53
54
  # Resources not yet implemented in SDK
54
55
  "offices": None,
55
56
  }
@@ -66,6 +67,7 @@ def get_shape_config_entries() -> list[tuple[str, str, type[Any]]]:
66
67
  Forecast,
67
68
  Grant,
68
69
  GsaElibraryContract,
70
+ ITDashboardInvestment,
69
71
  Notice,
70
72
  Opportunity,
71
73
  Organization,
@@ -98,6 +100,16 @@ def get_shape_config_entries() -> list[tuple[str, str, type[Any]]]:
98
100
  ShapeConfig.GSA_ELIBRARY_CONTRACTS_MINIMAL,
99
101
  GsaElibraryContract,
100
102
  ),
103
+ (
104
+ "ITDASHBOARD_INVESTMENTS_MINIMAL",
105
+ ShapeConfig.ITDASHBOARD_INVESTMENTS_MINIMAL,
106
+ ITDashboardInvestment,
107
+ ),
108
+ (
109
+ "ITDASHBOARD_INVESTMENTS_COMPREHENSIVE",
110
+ ShapeConfig.ITDASHBOARD_INVESTMENTS_COMPREHENSIVE,
111
+ ITDashboardInvestment,
112
+ ),
101
113
  ]
102
114
  for name, shape_str, model_cls in configs:
103
115
  entries.append((name, shape_str, model_cls))
@@ -10,6 +10,7 @@ from .exceptions import (
10
10
  )
11
11
  from .models import (
12
12
  GsaElibraryContract,
13
+ ITDashboardInvestment,
13
14
  PaginatedResponse,
14
15
  RateLimitInfo,
15
16
  SearchFilters,
@@ -28,7 +29,7 @@ from .shapes import (
28
29
  TypeGenerator,
29
30
  )
30
31
 
31
- __version__ = "0.4.3"
32
+ __version__ = "0.5.0"
32
33
  __all__ = [
33
34
  "TangoClient",
34
35
  "TangoAPIError",
@@ -38,6 +39,7 @@ __all__ = [
38
39
  "TangoRateLimitError",
39
40
  "RateLimitInfo",
40
41
  "GsaElibraryContract",
42
+ "ITDashboardInvestment",
41
43
  "PaginatedResponse",
42
44
  "SearchFilters",
43
45
  "ShapeConfig",
@@ -26,6 +26,7 @@ from tango.models import (
26
26
  Forecast,
27
27
  Grant,
28
28
  GsaElibraryContract,
29
+ ITDashboardInvestment,
29
30
  Location,
30
31
  Notice,
31
32
  Opportunity,
@@ -59,6 +60,8 @@ class TangoClient:
59
60
  self,
60
61
  api_key: str | None = None,
61
62
  base_url: str = "https://tango.makegov.com",
63
+ user_agent: str | None = None,
64
+ extra_headers: dict[str, str] | None = None,
62
65
  ):
63
66
  """
64
67
  Initialize the Tango API client
@@ -67,6 +70,8 @@ class TangoClient:
67
70
  api_key: API key for authentication. If not provided, will attempt to load from
68
71
  TANGO_API_KEY environment variable.
69
72
  base_url: Base URL for the API
73
+ user_agent: Custom User-Agent header value.
74
+ extra_headers: Additional headers to include in every request.
70
75
  """
71
76
  # Load API key from environment if not provided
72
77
  self.api_key = api_key or os.getenv("TANGO_API_KEY")
@@ -76,9 +81,14 @@ class TangoClient:
76
81
  headers = {}
77
82
  if self.api_key:
78
83
  headers["X-API-KEY"] = self.api_key
84
+ if user_agent:
85
+ headers["User-Agent"] = user_agent
86
+ if extra_headers:
87
+ headers.update(extra_headers)
79
88
 
80
89
  self.client = httpx.Client(headers=headers, timeout=30.0)
81
90
  self._last_rate_limit_info: RateLimitInfo | None = None
91
+ self._last_response_headers: httpx.Headers | None = None
82
92
 
83
93
  # Use hardcoded sensible defaults
84
94
  cache_size = 100
@@ -105,6 +115,11 @@ class TangoClient:
105
115
  """Rate limit info from the most recent API response."""
106
116
  return self._last_rate_limit_info
107
117
 
118
+ @property
119
+ def last_response_headers(self) -> httpx.Headers | None:
120
+ """Full HTTP headers from the most recent API response."""
121
+ return self._last_response_headers
122
+
108
123
  @staticmethod
109
124
  def _parse_rate_limit_headers(headers: httpx.Headers) -> RateLimitInfo:
110
125
  """Extract rate limit info from response headers."""
@@ -140,6 +155,7 @@ class TangoClient:
140
155
 
141
156
  try:
142
157
  response = self.client.request(method=method, url=url, params=params, json=json_data)
158
+ self._last_response_headers = response.headers
143
159
  self._last_rate_limit_info = self._parse_rate_limit_headers(response.headers)
144
160
 
145
161
  if response.status_code == 401:
@@ -1321,6 +1337,112 @@ class TangoClient:
1321
1337
  data, shape, GsaElibraryContract, flat, flat_lists, joiner=joiner
1322
1338
  )
1323
1339
 
1340
+ # ============================================================================
1341
+ # IT Dashboard Investments
1342
+ # ============================================================================
1343
+
1344
+ def list_itdashboard_investments(
1345
+ self,
1346
+ page: int = 1,
1347
+ limit: int = 25,
1348
+ shape: str | None = None,
1349
+ flat: bool = False,
1350
+ flat_lists: bool = False,
1351
+ joiner: str = ".",
1352
+ search: str | None = None,
1353
+ agency_code: int | None = None,
1354
+ agency_name: str | None = None,
1355
+ type_of_investment: str | None = None,
1356
+ updated_time_after: str | date | datetime | None = None,
1357
+ updated_time_before: str | date | datetime | None = None,
1358
+ cio_rating: int | None = None,
1359
+ cio_rating_max: int | None = None,
1360
+ performance_risk: bool | None = None,
1361
+ ) -> PaginatedResponse:
1362
+ """List federal IT investments from the IT Dashboard (`/api/itdashboard/`).
1363
+
1364
+ Filters are tier-gated by the API:
1365
+
1366
+ - **Free**: ``search`` (full-text across UII, title, description, agency, bureau)
1367
+ - **Pro**: ``agency_code``, ``type_of_investment``,
1368
+ ``updated_time_after`` / ``updated_time_before``
1369
+ - **Business+**: ``agency_name`` (text), ``cio_rating``,
1370
+ ``cio_rating_max``, ``performance_risk``
1371
+
1372
+ Hitting a gated filter on a lower tier returns a 403 with upgrade info.
1373
+
1374
+ CIO ratings: 1=High Risk, 2=Moderately High, 3=Medium, 4=Moderately Low, 5=Low.
1375
+ ``performance_risk=True`` returns investments with at least one NOT MET metric.
1376
+ """
1377
+ params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
1378
+ if shape is None:
1379
+ shape = ShapeConfig.ITDASHBOARD_INVESTMENTS_MINIMAL
1380
+ if shape:
1381
+ params["shape"] = shape
1382
+ if flat:
1383
+ params["flat"] = "true"
1384
+ if joiner:
1385
+ params["joiner"] = joiner
1386
+ if flat_lists:
1387
+ params["flat_lists"] = "true"
1388
+ for k, val in (
1389
+ ("search", search),
1390
+ ("agency_code", agency_code),
1391
+ ("agency_name", agency_name),
1392
+ ("type_of_investment", type_of_investment),
1393
+ ("updated_time_after", updated_time_after),
1394
+ ("updated_time_before", updated_time_before),
1395
+ ("cio_rating", cio_rating),
1396
+ ("cio_rating_max", cio_rating_max),
1397
+ ("performance_risk", performance_risk),
1398
+ ):
1399
+ if val is None:
1400
+ continue
1401
+ if isinstance(val, bool):
1402
+ params[k] = "true" if val else "false"
1403
+ elif isinstance(val, (date, datetime)):
1404
+ params[k] = val.isoformat()
1405
+ else:
1406
+ params[k] = val
1407
+ data = self._get("/api/itdashboard/", params)
1408
+ results = [
1409
+ self._parse_response_with_shape(
1410
+ obj, shape, ITDashboardInvestment, flat, flat_lists, joiner=joiner
1411
+ )
1412
+ for obj in data.get("results", [])
1413
+ ]
1414
+ return PaginatedResponse(
1415
+ count=data.get("count", 0),
1416
+ next=data.get("next"),
1417
+ previous=data.get("previous"),
1418
+ results=results,
1419
+ )
1420
+
1421
+ def get_itdashboard_investment(
1422
+ self,
1423
+ uii: str,
1424
+ shape: str | None = None,
1425
+ flat: bool = False,
1426
+ flat_lists: bool = False,
1427
+ joiner: str = ".",
1428
+ ) -> Any:
1429
+ """Get a single IT Dashboard investment by UII (`/api/itdashboard/{uii}/`)."""
1430
+ params: dict[str, Any] = {}
1431
+ if shape is None:
1432
+ shape = ShapeConfig.ITDASHBOARD_INVESTMENTS_COMPREHENSIVE
1433
+ if shape:
1434
+ params["shape"] = shape
1435
+ if flat:
1436
+ params["flat"] = "true"
1437
+ if joiner:
1438
+ params["joiner"] = joiner
1439
+ if flat_lists:
1440
+ params["flat_lists"] = "true"
1441
+ data = self._get(f"/api/itdashboard/{uii}/", params)
1442
+ return self._parse_response_with_shape(
1443
+ data, shape, ITDashboardInvestment, flat, flat_lists, joiner=joiner
1444
+ )
1445
+
1324
1446
  # ============================================================================
1325
1447
  # Vehicles (Awards)
1326
1448
  # ============================================================================
@@ -367,6 +367,45 @@ class GsaElibraryContract:
367
367
  sins: list[str] | None = None
368
368
 
369
369
 
370
+ @dataclass
371
+ class ITDashboardInvestment:
372
+ """Schema definition for IT Dashboard Investment (not used for instances)
373
+
374
+ Federal IT investment from itdashboard.gov, exposed at /api/itdashboard/.
375
+ Identified by ``uii`` (Unique Investment Identifier).
376
+
377
+ Tier-gated shape expansions:
378
+ Free base fields only
379
+ Pro+ ``funding`` and ``details`` expansions
380
+ Business+ nested sub-tables (``cio_evaluation``, ``contracts``,
381
+ ``projects``, ``cost_pools_towers``, ``funding_sources``,
382
+ ``performance_metrics``, ``performance_actual``,
383
+ ``operational_analysis``) and ``business_case_html``
384
+ """
385
+
386
+ uii: str
387
+ agency_code: int | None = None
388
+ agency_name: str | None = None
389
+ bureau_code: int | None = None
390
+ bureau_name: str | None = None
391
+ investment_title: str | None = None
392
+ type_of_investment: str | None = None
393
+ part_of_it_portfolio: str | None = None
394
+ updated_time: datetime | None = None
395
+ url: str | None = None
396
+ business_case_html: str | None = None
397
+ funding: dict[str, Any] | None = None
398
+ details: dict[str, Any] | None = None
399
+ cio_evaluation: list[dict[str, Any]] | None = None
400
+ contracts: list[dict[str, Any]] | None = None
401
+ projects: list[dict[str, Any]] | None = None
402
+ cost_pools_towers: list[dict[str, Any]] | None = None
403
+ funding_sources: list[dict[str, Any]] | None = None
404
+ performance_metrics: list[dict[str, Any]] | None = None
405
+ performance_actual: list[dict[str, Any]] | None = None
406
+ operational_analysis: list[dict[str, Any]] | None = None
407
+
408
+
370
409
  @dataclass
371
410
  class Vehicle:
372
411
  """Schema definition for Vehicle (not used for instances)"""
@@ -687,3 +726,18 @@ class ShapeConfig:
687
726
  GSA_ELIBRARY_CONTRACTS_MINIMAL: Final = (
688
727
  "uuid,contract_number,schedule,recipient(display_name,uei),idv(key,award_date)"
689
728
  )
729
+
730
+ # Default for list_itdashboard_investments()
731
+ # Free-tier safe: matches the API's INVESTMENT_LIST_DEFAULT_SHAPE.
732
+ ITDASHBOARD_INVESTMENTS_MINIMAL: Final = (
733
+ "uii,agency_name,bureau_name,investment_title,"
734
+ "type_of_investment,part_of_it_portfolio,updated_time,url"
735
+ )
736
+
737
+ # Default for get_itdashboard_investment()
738
+ # Free-tier safe: matches the API's INVESTMENT_RETRIEVE_DEFAULT_SHAPE.
739
+ ITDASHBOARD_INVESTMENTS_COMPREHENSIVE: Final = (
740
+ "uii,agency_code,agency_name,bureau_code,bureau_name,"
741
+ "investment_title,type_of_investment,part_of_it_portfolio,"
742
+ "updated_time,url"
743
+ )
@@ -1132,6 +1132,67 @@ GSA_ELIBRARY_CONTRACT_SCHEMA: dict[str, FieldSchema] = {
1132
1132
  ),
1133
1133
  }
1134
1134
 
1135
+ # IT Dashboard Investment
1136
+ ITDASHBOARD_INVESTMENT_SCHEMA: dict[str, FieldSchema] = {
1137
+ "uii": FieldSchema(name="uii", type=str, is_optional=False, is_list=False),
1138
+ "agency_code": FieldSchema(
1139
+ name="agency_code", type=int, is_optional=True, is_list=False
1140
+ ),
1141
+ "agency_name": FieldSchema(
1142
+ name="agency_name", type=str, is_optional=True, is_list=False
1143
+ ),
1144
+ "bureau_code": FieldSchema(
1145
+ name="bureau_code", type=int, is_optional=True, is_list=False
1146
+ ),
1147
+ "bureau_name": FieldSchema(
1148
+ name="bureau_name", type=str, is_optional=True, is_list=False
1149
+ ),
1150
+ "investment_title": FieldSchema(
1151
+ name="investment_title", type=str, is_optional=True, is_list=False
1152
+ ),
1153
+ "type_of_investment": FieldSchema(
1154
+ name="type_of_investment", type=str, is_optional=True, is_list=False
1155
+ ),
1156
+ "part_of_it_portfolio": FieldSchema(
1157
+ name="part_of_it_portfolio", type=str, is_optional=True, is_list=False
1158
+ ),
1159
+ "updated_time": FieldSchema(
1160
+ name="updated_time", type=datetime, is_optional=True, is_list=False
1161
+ ),
1162
+ "url": FieldSchema(name="url", type=str, is_optional=True, is_list=False),
1163
+ "business_case_html": FieldSchema(
1164
+ name="business_case_html", type=str, is_optional=True, is_list=False
1165
+ ),
1166
+ # Expansions: dict (funding/details) and list-of-dict (nested sub-tables).
1167
+ # Modeled as opaque dict/list since their inner shapes are dynamic.
1168
+ "funding": FieldSchema(name="funding", type=dict, is_optional=True, is_list=False),
1169
+ "details": FieldSchema(name="details", type=dict, is_optional=True, is_list=False),
1170
+ "cio_evaluation": FieldSchema(
1171
+ name="cio_evaluation", type=list, is_optional=True, is_list=True
1172
+ ),
1173
+ "contracts": FieldSchema(
1174
+ name="contracts", type=list, is_optional=True, is_list=True
1175
+ ),
1176
+ "projects": FieldSchema(
1177
+ name="projects", type=list, is_optional=True, is_list=True
1178
+ ),
1179
+ "cost_pools_towers": FieldSchema(
1180
+ name="cost_pools_towers", type=list, is_optional=True, is_list=True
1181
+ ),
1182
+ "funding_sources": FieldSchema(
1183
+ name="funding_sources", type=list, is_optional=True, is_list=True
1184
+ ),
1185
+ "performance_metrics": FieldSchema(
1186
+ name="performance_metrics", type=list, is_optional=True, is_list=True
1187
+ ),
1188
+ "performance_actual": FieldSchema(
1189
+ name="performance_actual", type=list, is_optional=True, is_list=True
1190
+ ),
1191
+ "operational_analysis": FieldSchema(
1192
+ name="operational_analysis", type=list, is_optional=True, is_list=True
1193
+ ),
1194
+ }
1195
+
1135
1196
  # ============================================================================
1136
1197
  # SCHEMA REGISTRY MAPPING
1137
1198
  # ============================================================================
@@ -1176,6 +1237,8 @@ EXPLICIT_SCHEMAS: dict[str, dict[str, FieldSchema]] = {
1176
1237
  # GSA eLibrary
1177
1238
  "GsaElibraryContract": GSA_ELIBRARY_CONTRACT_SCHEMA,
1178
1239
  "GsaElibraryIdvRef": GSA_ELIBRARY_IDV_REF_SCHEMA,
1240
+ # IT Dashboard
1241
+ "ITDashboardInvestment": ITDASHBOARD_INVESTMENT_SCHEMA,
1179
1242
  }
1180
1243
 
1181
1244
 
@@ -0,0 +1,95 @@
1
+ interactions:
2
+ - request:
3
+ body: ''
4
+ headers:
5
+ accept:
6
+ - '*/*'
7
+ accept-encoding:
8
+ - gzip, deflate
9
+ connection:
10
+ - keep-alive
11
+ host:
12
+ - tango.makegov.com
13
+ user-agent:
14
+ - python-httpx/0.28.1
15
+ method: GET
16
+ uri: https://tango.makegov.com/api/itdashboard/?page=1&limit=5&shape=uii%2Cagency_name%2Cbureau_name%2Cinvestment_title%2Ctype_of_investment%2Cpart_of_it_portfolio%2Cupdated_time%2Curl&agency_code=21
17
+ response:
18
+ body:
19
+ string: '{"count":360,"next":"https://tango.makegov.com/api/itdashboard/?agency_code=21&limit=5&page=2&shape=uii%2Cagency_name%2Cbureau_name%2Cinvestment_title%2Ctype_of_investment%2Cpart_of_it_portfolio%2Cupdated_time%2Curl","previous":null,"results":[{"uii":"021-777552743","agency_name":"Department
20
+ of Transportation","bureau_name":"National Highway Traffic Safety Administration","investment_title":"NHTSA0020:
21
+ Artemis","type_of_investment":"Major IT Investments","part_of_it_portfolio":"Part
22
+ 1. IT Investments for Mission Delivery","updated_time":"2026-03-27T12:45:58+00:00","url":"https://www.itdashboard.gov/investment-details/021-777552743"},{"uii":"021-302996650","agency_name":"Department
23
+ of Transportation","bureau_name":"National Highway Traffic Safety Administration","investment_title":"NHTSA0347:
24
+ CDAN Version 2.0","type_of_investment":"Major IT Investments","part_of_it_portfolio":"Part
25
+ 1. IT Investments for Mission Delivery","updated_time":"2026-03-26T16:50:10+00:00","url":"https://www.itdashboard.gov/investment-details/021-302996650"},{"uii":"021-165719668","agency_name":"Department
26
+ of Transportation","bureau_name":"National Highway Traffic Safety Administration","investment_title":"NHTSA0301:
27
+ Teleprocessing and Timesharing Services for the NDR Program","type_of_investment":"Major
28
+ IT Investments","part_of_it_portfolio":"Part 1. IT Investments for Mission
29
+ Delivery","updated_time":"2026-03-26T16:49:35+00:00","url":"https://www.itdashboard.gov/investment-details/021-165719668"},{"uii":"021-700323071","agency_name":"Department
30
+ of Transportation","bureau_name":"Maritime Administration","investment_title":"MARAD0071:
31
+ National Security Multi-Mission Vessel (NSMV)","type_of_investment":"Major
32
+ IT Investments","part_of_it_portfolio":"Part 1. IT Investments for Mission
33
+ Delivery","updated_time":"2026-03-25T20:04:19+00:00","url":"https://www.itdashboard.gov/investment-details/021-700323071"},{"uii":"021-700323070","agency_name":"Department
34
+ of Transportation","bureau_name":"Maritime Administration","investment_title":"MARAD0070:
35
+ USMMA Campus Solutions (UCAS)","type_of_investment":"Major IT Investments","part_of_it_portfolio":"Part
36
+ 1. IT Investments for Mission Delivery","updated_time":"2026-03-25T20:01:19+00:00","url":"https://www.itdashboard.gov/investment-details/021-700323070"}]}'
37
+ headers:
38
+ CF-RAY:
39
+ - 9e91f5012a290a26-ORD
40
+ Connection:
41
+ - keep-alive
42
+ Content-Type:
43
+ - application/json
44
+ Date:
45
+ - Wed, 08 Apr 2026 14:28:11 GMT
46
+ Nel:
47
+ - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
48
+ Report-To:
49
+ - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=3I21M9xblZ6mvJlcfLRR%2BgHPPkc4av1Jh5BnY2zzrEEQYRrjfLQYZ4wTjxthr51w2nNIPM4ww3TdtKdPPM%2BaPRa4lwu7%2F7SO1IC%2FtbBfAEYkTD6KuUhLQTtsgw8tF%2BSst8l8wPVEBcI8NpySZKvr"}]}'
50
+ Server:
51
+ - cloudflare
52
+ Transfer-Encoding:
53
+ - chunked
54
+ allow:
55
+ - GET, HEAD, OPTIONS
56
+ cf-cache-status:
57
+ - DYNAMIC
58
+ content-length:
59
+ - '2305'
60
+ cross-origin-opener-policy:
61
+ - same-origin
62
+ referrer-policy:
63
+ - same-origin
64
+ vary:
65
+ - Accept, Cookie
66
+ x-content-type-options:
67
+ - nosniff
68
+ x-execution-time:
69
+ - 0.024s
70
+ x-frame-options:
71
+ - DENY
72
+ x-ratelimit-burst-limit:
73
+ - '1000'
74
+ x-ratelimit-burst-remaining:
75
+ - '991'
76
+ x-ratelimit-burst-reset:
77
+ - '54'
78
+ x-ratelimit-daily-limit:
79
+ - '2000000'
80
+ x-ratelimit-daily-remaining:
81
+ - '1999933'
82
+ x-ratelimit-daily-reset:
83
+ - '34308'
84
+ x-ratelimit-limit:
85
+ - '1000'
86
+ x-ratelimit-remaining:
87
+ - '991'
88
+ x-ratelimit-reset:
89
+ - '54'
90
+ x-results-counttype:
91
+ - exact
92
+ status:
93
+ code: 200
94
+ message: OK
95
+ version: 1
@@ -0,0 +1,93 @@
1
+ interactions:
2
+ - request:
3
+ body: ''
4
+ headers:
5
+ accept:
6
+ - '*/*'
7
+ accept-encoding:
8
+ - gzip, deflate
9
+ connection:
10
+ - keep-alive
11
+ host:
12
+ - tango.makegov.com
13
+ user-agent:
14
+ - python-httpx/0.28.1
15
+ method: GET
16
+ uri: https://tango.makegov.com/api/itdashboard/?page=1&limit=5&shape=uii%2Cagency_name%2Cbureau_name%2Cinvestment_title%2Ctype_of_investment%2Cpart_of_it_portfolio%2Cupdated_time%2Curl&agency_name=defense
17
+ response:
18
+ body:
19
+ string: '{"count":2655,"next":"https://tango.makegov.com/api/itdashboard/?agency_name=defense&limit=5&page=2&shape=uii%2Cagency_name%2Cbureau_name%2Cinvestment_title%2Ctype_of_investment%2Cpart_of_it_portfolio%2Cupdated_time%2Curl","previous":null,"results":[{"uii":"007-000103860","agency_name":"Department
20
+ of Defense","bureau_name":"Army","investment_title":"ENTERPRISE BUSINESS SYSTEMS-CONVERGENCE","type_of_investment":"Major
21
+ IT Investments","part_of_it_portfolio":"Part 2. IT Investments for Mission
22
+ Support Services","updated_time":"2026-02-24T15:47:57+00:00","url":"https://www.itdashboard.gov/investment-details/007-000103860"},{"uii":"007-000103486","agency_name":"Department
23
+ of Defense","bureau_name":"Navy, Marine Corps","investment_title":"Naval -
24
+ Maintenance, Repair, and Overhaul","type_of_investment":"Major IT Investments","part_of_it_portfolio":"Part
25
+ 2. IT Investments for Mission Support Services","updated_time":"2026-02-24T15:47:56+00:00","url":"https://www.itdashboard.gov/investment-details/007-000103486"},{"uii":"007-000103389","agency_name":"Department
26
+ of Defense","bureau_name":"Navy, Marine Corps","investment_title":"Navy Personnel
27
+ and Pay","type_of_investment":"Major IT Investments","part_of_it_portfolio":"Part
28
+ 2. IT Investments for Mission Support Services","updated_time":"2026-02-24T15:47:55+00:00","url":"https://www.itdashboard.gov/investment-details/007-000103389"},{"uii":"007-000101162","agency_name":"Department
29
+ of Defense","bureau_name":"Defense-wide","investment_title":"DEFENSE AGENCIES
30
+ INITIATIVE","type_of_investment":"Major IT Investments","part_of_it_portfolio":"Part
31
+ 2. IT Investments for Mission Support Services","updated_time":"2026-02-24T15:47:53+00:00","url":"https://www.itdashboard.gov/investment-details/007-000101162"},{"uii":"007-000101099","agency_name":"Department
32
+ of Defense","bureau_name":"Defense-wide","investment_title":"Advancing Analytics","type_of_investment":"Major
33
+ IT Investments","part_of_it_portfolio":"Part 2. IT Investments for Mission
34
+ Support Services","updated_time":"2026-02-24T15:47:52+00:00","url":"https://www.itdashboard.gov/investment-details/007-000101099"}]}'
35
+ headers:
36
+ CF-RAY:
37
+ - 9e91f50efa00eb60-ORD
38
+ Connection:
39
+ - keep-alive
40
+ Content-Type:
41
+ - application/json
42
+ Date:
43
+ - Wed, 08 Apr 2026 14:28:13 GMT
44
+ Nel:
45
+ - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
46
+ Report-To:
47
+ - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=sVGRzZBXm8exykIK%2FejvOiYFHW4PdBAjQzVz8kFHaHvTKCThUTCPOdvSEmryfb1TOd2h6HWHaT4KLsBDgJFFcRVZqkYNjm32asNjoEnEg0wBrzakieSIJwfowtZYZjmpTn8HD%2BUTJzKIKlf6QARk"}]}'
48
+ Server:
49
+ - cloudflare
50
+ Transfer-Encoding:
51
+ - chunked
52
+ allow:
53
+ - GET, HEAD, OPTIONS
54
+ cf-cache-status:
55
+ - DYNAMIC
56
+ content-length:
57
+ - '2133'
58
+ cross-origin-opener-policy:
59
+ - same-origin
60
+ referrer-policy:
61
+ - same-origin
62
+ vary:
63
+ - Accept, Cookie
64
+ x-content-type-options:
65
+ - nosniff
66
+ x-execution-time:
67
+ - 0.023s
68
+ x-frame-options:
69
+ - DENY
70
+ x-ratelimit-burst-limit:
71
+ - '1000'
72
+ x-ratelimit-burst-remaining:
73
+ - '988'
74
+ x-ratelimit-burst-reset:
75
+ - '52'
76
+ x-ratelimit-daily-limit:
77
+ - '2000000'
78
+ x-ratelimit-daily-remaining:
79
+ - '1999930'
80
+ x-ratelimit-daily-reset:
81
+ - '34306'
82
+ x-ratelimit-limit:
83
+ - '1000'
84
+ x-ratelimit-remaining:
85
+ - '988'
86
+ x-ratelimit-reset:
87
+ - '52'
88
+ x-results-counttype:
89
+ - exact
90
+ status:
91
+ code: 200
92
+ message: OK
93
+ version: 1