tango-python 1.5.1__tar.gz → 1.6.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.
- {tango_python-1.5.1 → tango_python-1.6.0}/CHANGELOG.md +19 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/PKG-INFO +2 -2
- {tango_python-1.5.1 → tango_python-1.6.0}/docs/API_REFERENCE.md +203 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/docs/WEBHOOKS.md +15 -5
- {tango_python-1.5.1 → tango_python-1.6.0}/pyproject.toml +1 -1
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/check_filter_shape_conformance.py +2 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/check_shape_coverage.py +2 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/__init__.py +7 -1
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/client.py +452 -1
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/models.py +164 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/generated_overlay.py +506 -45
- tango_python-1.6.0/tests/test_sled.py +281 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/.gitignore +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/LICENSE +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/README.md +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/docs/CLIENT.md +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/docs/DYNAMIC_MODELS.md +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/docs/ERRORS.md +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/docs/PAGINATION.md +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/docs/SHAPES.md +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/README.md +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/fetch_api_schema.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/generate_schemas_from_api.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/generate_shape_overlay.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/pr_review.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/probe_shape_types.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/refresh_contract.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/smoke_api_parity.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/scripts/test_production.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/exceptions.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/__init__.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/explicit_schemas.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/factory.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/generator.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/models.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/parser.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/schema.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/shapes/types.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/webhooks/__init__.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/webhooks/cli.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/webhooks/receiver.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/webhooks/signing.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tango/webhooks/simulate.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/__init__.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_budget_flows +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[minimal-case_id,case_number,title,source_system,outcome,filed_date] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.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
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/conftest.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/README.md +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/__init__.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/conftest.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_agencies_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_contracts_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_edge_cases_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_entities_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_forecasts_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_grants_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_itdashboard_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_naics_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_notices_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_offices_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_opportunities_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_organizations_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_protests_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_reference_data_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_subawards_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/integration/validation.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/production/__init__.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/production/conftest.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/production/test_production_smoke.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_api_parity.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_client.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_dibbs_exclusions_sbir.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_models.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_shape_coverage_gate.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_shapes.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_webhooks_cli.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_webhooks_receiver.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_webhooks_signing.py +0 -0
- {tango_python-1.5.1 → tango_python-1.6.0}/tests/test_webhooks_simulate.py +0 -0
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.6.0] - 2026-09-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`attachments(extracted_text)` — SLED document bodies on the Small plan and above** (Tango API 4.25.1). Re-vendored the contract and regenerated the shape overlay, so `shape="...,attachments(name,extracted_text)"` now validates instead of being rejected client-side. Three properties are documented on the model and in `docs/API_REFERENCE.md`, because each is otherwise a support question: the leaf must be **named** (neither `SLED_OPPORTUNITIES_MINIMAL` nor `SLED_OPPORTUNITIES_COMPREHENSIVE` includes it, and `attachments(*)` does not carry it, because the API only resolves the body for a caller who asked); the **key is absent rather than null** when the text is not being served, so read it with `.get()`; and a **contested document never returns text at any plan**. Searching document text stays ungated on every plan and returns no fragment of it — buying the body does not change search.
|
|
14
|
+
- **State, local and education (SLED) procurement** (Tango API 4.25.0). Six methods over the new `/api/sled/` namespace: `list_sled_opportunities()`, `get_sled_opportunity()`, `list_sled_opportunity_revisions()`, `get_sled_coverage()`, `list_sled_forecasts()`, `get_sled_forecast()`, plus `SledOpportunity` / `SledOpportunityRevision` / `SledForecast` schemas and five `ShapeConfig` defaults. Every one of the API's 27 solicitation filters and 13 forecast filters is an explicit named parameter.
|
|
15
|
+
|
|
16
|
+
Four behaviors are documented on the methods themselves because each misleads a caller who assumes federal semantics. **Passing neither `status` nor `active` returns open solicitations only** — that default is the API's, and the SDK deliberately does not synthesize a `status` param, since doing so would make `active=False` unreachable (pinned by a test). **`status` is Tango-derived and refreshed every fifteen minutes**; the portal's own word is served as `source_status`, is frozen at last capture, and is not a liveness filter. **Category scheme tagging is mid-migration**, so `naics=` matches only the small tagged share and `category_code=` is the escape hatch. And **`meta.attachment_count` can be lower than `len(attachments)`**, because an auto-generated portal cover sheet is listed and flagged `is_generated_summary` but excluded from the count.
|
|
17
|
+
|
|
18
|
+
`SLED_REVISIONS_MINIMAL` omits `changes` on purpose: the per-field before/after needs a Small plan, so naming it by default would 403 a Free caller. `changed_fields` is in the default and available at every plan.
|
|
19
|
+
- **`previous_uii` on `list_itdashboard_investments()`.** The Tango API has accepted this filter for a while — it resolves a retired UII forward to whatever superseded it — and the SDK never picked it up, so it was a silent no-op for anyone who passed it through. Ungated, unlike its analytical neighbours on that endpoint. Found by the SDK's own filter-conformance check, not by SLED work.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Re-vendored `contracts/filter_shape_contract.json` (schema_version 2, 48 resources) and regenerated `tango/shapes/generated_overlay.py` from it — 535 fields across 33 containers, 84 nested schemas. Besides the SLED expands this closes three entity gaps the older contract could not see: `relationships(confidence, verification_method)` and the whole `workforce(*)` expand were reachable on the API and rejected client-side by the SDK's schema.
|
|
23
|
+
- Baselined 14 reverse-shape-coverage gaps in `contracts/shape_coverage_baseline.json`. All 14 are nested sub-resource routes the newer contract publishes for the first time (`entities_contracts`, `idvs_awards`, `vehicles_orders`, and similar); none is SLED, and none is a regression — they are pre-existing SDK gaps that only became visible with the re-vendored contract. The gate reports 0 new drift.
|
|
24
|
+
|
|
25
|
+
### Documentation
|
|
26
|
+
- New **State & Local (SLED)** section in `docs/API_REFERENCE.md` covering all six methods, both defaults that surprise people, and the five new `ShapeConfig` constants.
|
|
27
|
+
- Corrected `docs/WEBHOOKS.md`. It stated flatly that "nothing fires when a record merely lapses"; that is now false for exactly one query type. `alerts.sled_opportunity.match` **does** fire on a closing, because SLED liveness is a stored column a fifteen-minute sweep writes rather than a query-time derivation. Also documented that there is no `sled_forecast` query type and that SLED revisions and attachments are not separately alertable.
|
|
28
|
+
|
|
10
29
|
## [1.5.1] - 2026-08-10
|
|
11
30
|
|
|
12
31
|
### Documentation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: tango-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.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
|
|
@@ -18,6 +18,7 @@ Complete reference for all Tango Python SDK methods and functionality.
|
|
|
18
18
|
- [Forecasts](#forecasts)
|
|
19
19
|
- [Opportunities](#opportunities)
|
|
20
20
|
- [Notices](#notices)
|
|
21
|
+
- [State \& Local (SLED)](#state--local-sled)
|
|
21
22
|
- [Grants](#grants)
|
|
22
23
|
- [GSA eLibrary Contracts](#gsa-elibrary-contracts)
|
|
23
24
|
- [Protests](#protests)
|
|
@@ -1213,6 +1214,203 @@ protest = client.get_protest(
|
|
|
1213
1214
|
|
|
1214
1215
|
---
|
|
1215
1216
|
|
|
1217
|
+
## State & Local (SLED)
|
|
1218
|
+
|
|
1219
|
+
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.
|
|
1220
|
+
|
|
1221
|
+
This data does not join to the federal data. There is no UEI, no PIID, no agency-hierarchy key and no NAICS/PSC crosswalk; `organization(*)` here is three strings, not the federal 7-key office payload.
|
|
1222
|
+
|
|
1223
|
+
### list_sled_opportunities()
|
|
1224
|
+
|
|
1225
|
+
List SLED solicitations with filtering and shaping.
|
|
1226
|
+
|
|
1227
|
+
```python
|
|
1228
|
+
solicitations = client.list_sled_opportunities(
|
|
1229
|
+
page=1,
|
|
1230
|
+
limit=25,
|
|
1231
|
+
shape=ShapeConfig.SLED_OPPORTUNITIES_MINIMAL,
|
|
1232
|
+
# Filter parameters (all optional)
|
|
1233
|
+
state=None,
|
|
1234
|
+
jurisdiction=None,
|
|
1235
|
+
status=None,
|
|
1236
|
+
active=None,
|
|
1237
|
+
agency=None,
|
|
1238
|
+
solicitation_number=None,
|
|
1239
|
+
solicitation_type=None,
|
|
1240
|
+
has_documents=None,
|
|
1241
|
+
revision_kind=None,
|
|
1242
|
+
naics=None,
|
|
1243
|
+
nigp=None,
|
|
1244
|
+
unspsc=None,
|
|
1245
|
+
category=None,
|
|
1246
|
+
category_code=None,
|
|
1247
|
+
posted_after=None,
|
|
1248
|
+
posted_before=None,
|
|
1249
|
+
response_deadline_after=None,
|
|
1250
|
+
response_deadline_before=None,
|
|
1251
|
+
first_seen_after=None,
|
|
1252
|
+
first_seen_before=None,
|
|
1253
|
+
change_seen_after=None,
|
|
1254
|
+
modified_after=None,
|
|
1255
|
+
modified_before=None,
|
|
1256
|
+
search=None,
|
|
1257
|
+
ordering=None,
|
|
1258
|
+
)
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
**Two defaults worth knowing before your first call:**
|
|
1262
|
+
|
|
1263
|
+
- **Passing neither `status` nor `active` returns open solicitations only.** Only about a fifth of the corpus is open, and a portal drops a closed solicitation rather than restating it, so the API defaults the list to `status=open`. Pass an explicit `status` to page the whole corpus. `status="unknown"` (standing rosters, dateless RFIs) is hidden by that default — reach it with `status="open|unknown"`. `get_sled_opportunity()` returns the solicitation whatever its status.
|
|
1264
|
+
- **`status` is Tango's answer, not the portal's.** It is derived from the portal's word, the deadline and the clock, and refreshed every fifteen minutes. The portal's own word is served as `source_status` and is frozen at last capture — most of what it calls open already has a passed deadline. Never filter liveness on it.
|
|
1265
|
+
|
|
1266
|
+
**Filter Parameters:**
|
|
1267
|
+
- `state` - Two-letter state or territory code. Multi-value: `"TX|OK"`
|
|
1268
|
+
- `jurisdiction` - `"state"`, `"local"`, `"education"`, or `"unknown"` for aggregator rows that cannot tell state from local
|
|
1269
|
+
- `status` - `"open"`, `"closed"`, `"awarded"`, `"cancelled"`, `"unknown"`
|
|
1270
|
+
- `active` - Sugar for federal-shaped callers: `True` is `status="open"`; `False` is its complement, so it includes `unknown`
|
|
1271
|
+
- `agency` - Substring match on the buyer's published text (min 2 characters); no code resolution behind it
|
|
1272
|
+
- `solicitation_number` - The number a human would quote; null on roughly a third of the corpus
|
|
1273
|
+
- `solicitation_type` - `"rfp"`, `"ifb"`, `"rfq"`, `"rfi"`, `"itb"`, `"sole_source"`, `"grant"`, `"other"`. `"null"` (portal states no type) is a distinct answer from `"other"`
|
|
1274
|
+
- `has_documents` - Whether the solicitation advertises at least one document
|
|
1275
|
+
- `revision_kind` - Kind of the most recent substantive revision
|
|
1276
|
+
- `naics` / `nigp` / `unspsc` / `category` - Exact match within one category scheme. **`naics` is thin on purpose**: scheme tagging is mid-migration, so only a small share of entries are tagged NAICS
|
|
1277
|
+
- `category_code` - Match a code under ANY scheme, including the untagged pre-migration strings. The escape hatch when a scheme-specific filter returns less than expected
|
|
1278
|
+
- `posted_after` / `posted_before` - Posted-date range
|
|
1279
|
+
- `response_deadline_after` / `response_deadline_before` - Deadline range
|
|
1280
|
+
- `first_seen_after` / `first_seen_before` - When Tango first observed it. The polling primitive
|
|
1281
|
+
- `change_seen_after` - When Tango *observed* the last substantive change. A scrape date, not an amendment date
|
|
1282
|
+
- `modified_after` / `modified_before` - When the Tango row last changed
|
|
1283
|
+
- `platform` / `native_id` / `external_id` - Support filters for reproducing a record with us. Not in any response shape and not stable values
|
|
1284
|
+
- `search` - Ranked full-text search over title, agency, identifiers, category labels and description, widened by the solicitations whose *attachment text* matched (min 2 characters)
|
|
1285
|
+
- `ordering` - `rank`, `response_deadline`, `posted_date`, `first_seen_at`, `last_seen_at`, `last_change_seen_at`, `modified`. `rank` requires a non-empty `search`
|
|
1286
|
+
|
|
1287
|
+
**Returns:** [PaginatedResponse](#paginatedresponse) with solicitation dictionaries
|
|
1288
|
+
|
|
1289
|
+
**Example:**
|
|
1290
|
+
```python
|
|
1291
|
+
# Open Texas solicitations closing this month, newest first
|
|
1292
|
+
page = client.list_sled_opportunities(
|
|
1293
|
+
state="TX",
|
|
1294
|
+
response_deadline_before="2026-10-01",
|
|
1295
|
+
ordering="response_deadline",
|
|
1296
|
+
limit=25,
|
|
1297
|
+
)
|
|
1298
|
+
|
|
1299
|
+
for row in page.results:
|
|
1300
|
+
print(f"{row['state']} {row.get('solicitation_number') or '—'}: {row['title']}")
|
|
1301
|
+
|
|
1302
|
+
# Full-text search puts the matching passage on each row that matched on its body
|
|
1303
|
+
hits = client.list_sled_opportunities(
|
|
1304
|
+
search="environmental mitigation",
|
|
1305
|
+
shape="opportunity_id,title,state,snippet,response_deadline",
|
|
1306
|
+
)
|
|
1307
|
+
```
|
|
1308
|
+
|
|
1309
|
+
`snippet` is present only under `search=`, and only on rows that matched on their description — a title-or-agency match honestly carries none. Attachment matching contributes ids only: a caller learns *that* a document matched, never what it said.
|
|
1310
|
+
|
|
1311
|
+
### get_sled_opportunity()
|
|
1312
|
+
|
|
1313
|
+
Get a single solicitation by `opportunity_id`, whatever its status.
|
|
1314
|
+
|
|
1315
|
+
```python
|
|
1316
|
+
row = client.get_sled_opportunity(
|
|
1317
|
+
"OPPORTUNITY_UUID",
|
|
1318
|
+
shape="opportunity_id,title,status,attachments(*),revisions(*)",
|
|
1319
|
+
)
|
|
1320
|
+
```
|
|
1321
|
+
|
|
1322
|
+
**Notes:**
|
|
1323
|
+
- `meta.attachment_count` can be **lower** than `len(row["attachments"])`. Some portals auto-generate a cover sheet alongside the real documents; it is listed and flagged `is_generated_summary`, but excluded from the count and from `has_documents`. The count answers "does this hold its solicitation package"; the array answers "what files exist".
|
|
1324
|
+
- `size_bytes` and `char_count` only mean something as a pair — 3 MB that yielded no characters is a scan awaiting OCR.
|
|
1325
|
+
- `raw(*)` needs a Small plan or above, and is explicitly unstable: its shape varies by portal platform.
|
|
1326
|
+
|
|
1327
|
+
**Reading a document body — `attachments(extracted_text)`:**
|
|
1328
|
+
|
|
1329
|
+
```python
|
|
1330
|
+
row = client.get_sled_opportunity(
|
|
1331
|
+
opportunity_id,
|
|
1332
|
+
shape="opportunity_id,attachments(name,size_bytes,extracted_text)",
|
|
1333
|
+
)
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
Needs a **Small plan or above**, and Tango API 4.25.1 or later (`client.get_version()` reports what you are calling). Three rules:
|
|
1337
|
+
|
|
1338
|
+
- **You have to name it.** `attachments(*)` does not carry the body, and neither default shape names it — the API only resolves it for a caller who asked, so a default would make every detail fetch pay for a document nobody wanted to read.
|
|
1339
|
+
- **The key is absent, not null**, whenever the text is not being served to you: below Small (where it is withheld and named in `meta.upgrade_hints`), on a contested document, or where the text cannot be resolved. Use `attachment.get("extracted_text")`.
|
|
1340
|
+
- **A contested document never returns text**, at any plan — its stored bytes disagree with what the record advertised, so its text is not reliably that record's.
|
|
1341
|
+
|
|
1342
|
+
Searching document text and reading it are separate things. `search=` matches inside attachment text on **every plan** and returns no fragment of it; `extracted_text` is a per-record read on Small and above. Buying the body does not change search.
|
|
1343
|
+
|
|
1344
|
+
### list_sled_opportunity_revisions()
|
|
1345
|
+
|
|
1346
|
+
List one solicitation's observed revision history.
|
|
1347
|
+
|
|
1348
|
+
```python
|
|
1349
|
+
revisions = client.list_sled_opportunity_revisions(
|
|
1350
|
+
"OPPORTUNITY_UUID",
|
|
1351
|
+
kind=None,
|
|
1352
|
+
source_declared=None,
|
|
1353
|
+
observed_after=None,
|
|
1354
|
+
observed_before=None,
|
|
1355
|
+
)
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
**Notes:**
|
|
1359
|
+
- `observed_at` is the scrape that saw the change, not the date the agency made it. No state portal emits amendment notices, so `kind` is Tango's inference from the diff on about 95% of revisions, resolution is that state's crawl cadence, and history starts when Tango began reading the jurisdiction.
|
|
1360
|
+
- Unlike the `revisions(*)` expand, this route serves `enrichment` rows — Tango's own detail fetch filling in coverage rather than an agency amendment. Pass `kind="enrichment"` for only those.
|
|
1361
|
+
- `changes` (the per-field before and after) needs a Small plan; it is left out of the default shape for that reason. `changed_fields` names what moved at every plan.
|
|
1362
|
+
|
|
1363
|
+
### get_sled_coverage()
|
|
1364
|
+
|
|
1365
|
+
Get the per-state coverage rollup. Takes no parameters; not shaped or paginated.
|
|
1366
|
+
|
|
1367
|
+
```python
|
|
1368
|
+
coverage = client.get_sled_coverage()
|
|
1369
|
+
print(coverage["totals"])
|
|
1370
|
+
for row in coverage["states"]:
|
|
1371
|
+
print(row["state"], row["total_count"], row["by_status"], row["last_change_observed_at"])
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1374
|
+
**Call this before treating a per-state count as market size.** A thin result for a state is at least as likely to be a portal Tango does not read as a quiet market, and that is the ambiguity this endpoint exists to resolve. Every state row carries all five status buckets whether or not they have rows, so a total and two buckets never invite subtraction.
|
|
1375
|
+
|
|
1376
|
+
### list_sled_forecasts()
|
|
1377
|
+
|
|
1378
|
+
List planned state procurements.
|
|
1379
|
+
|
|
1380
|
+
```python
|
|
1381
|
+
forecasts = client.list_sled_forecasts(
|
|
1382
|
+
state=None,
|
|
1383
|
+
agency=None,
|
|
1384
|
+
procurement_category=None,
|
|
1385
|
+
procurement_method=None,
|
|
1386
|
+
contract_number=None,
|
|
1387
|
+
incumbent_name=None,
|
|
1388
|
+
advertisement_after=None,
|
|
1389
|
+
advertisement_before=None,
|
|
1390
|
+
first_seen_after=None,
|
|
1391
|
+
first_seen_before=None,
|
|
1392
|
+
modified_after=None,
|
|
1393
|
+
modified_before=None,
|
|
1394
|
+
search=None,
|
|
1395
|
+
ordering=None,
|
|
1396
|
+
)
|
|
1397
|
+
```
|
|
1398
|
+
|
|
1399
|
+
**Notes:**
|
|
1400
|
+
- **Forecasts carry no liveness at all** — no deadline to have passed, so there is no `status` field, no `active` filter, and no open-only default. Currency is the caller's call from `estimated_advertisement_date`.
|
|
1401
|
+
- `estimated_advertisement_date` is the **start of the published quarter**, not a posting date. `estimated_advertisement_raw` keeps the portal's own words (`"Q3 (Jan.-March 2027)"`), and a large share of rows publish no quarter at all.
|
|
1402
|
+
- `estimated_value(min,max,raw)` is parsed from a free-text award band at serve time. **A band naming one number is a floor, so `max` is `None`** — never read a missing `max` as an unbounded ceiling. `raw` is always there to check the parse against.
|
|
1403
|
+
- `incumbent_name` is published text, not a resolved Tango entity.
|
|
1404
|
+
- `ordering`: `rank`, `estimated_advertisement_date`, `first_seen_at`, `last_seen_at`, `modified`.
|
|
1405
|
+
|
|
1406
|
+
### get_sled_forecast()
|
|
1407
|
+
|
|
1408
|
+
```python
|
|
1409
|
+
forecast = client.get_sled_forecast("FORECAST_UUID")
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
---
|
|
1413
|
+
|
|
1216
1414
|
## Budget
|
|
1217
1415
|
|
|
1218
1416
|
Federal account × fiscal year budget rollups, covering the full budget lifecycle (requested → enacted → apportioned → obligated → outlayed), pre-computed ratios and trends, the contract / assistance / unlinked breakdown, and request-vs-actual spend.
|
|
@@ -2012,6 +2210,11 @@ entity = client.get_entity("UEI_KEY", shape=ShapeConfig.ENTITIES_COMPREHENSIVE)
|
|
|
2012
2210
|
| `VEHICLE_ORDERS_MINIMAL` | `list_vehicle_orders` | key, piid, award_date, recipient(display_name,uei), total_contract_value, obligated |
|
|
2013
2211
|
| `ITDASHBOARD_INVESTMENTS_MINIMAL` | `list_itdashboard_investments` | Minimal IT Dashboard investment fields |
|
|
2014
2212
|
| `ITDASHBOARD_INVESTMENTS_COMPREHENSIVE` | `get_itdashboard_investment` | Full investment fields: uii, agency_code, agency_name, bureau_code, bureau_name, investment_title, type_of_investment, part_of_it_portfolio, updated_time, url |
|
|
2213
|
+
| `SLED_OPPORTUNITIES_MINIMAL` | `list_sled_opportunities` | opportunity_id, solicitation_number, solicitation_type, title, state, jurisdiction, agency, status, status_reason, posted_date, response_deadline, source_url, has_documents, first_seen_at, last_change_seen_at (no `description` — detail-only on the API) |
|
|
2214
|
+
| `SLED_OPPORTUNITIES_COMPREHENSIVE` | `get_sled_opportunity` | Full solicitation with description, the raw portal status, both deadlines, bid opening, category codes, and organization / contact / meta / attachments / revisions |
|
|
2215
|
+
| `SLED_REVISIONS_MINIMAL` | `list_sled_opportunity_revisions` | observed_at, sequence, kind, changed_fields, source_declared (omits `changes`, which needs a Small plan) |
|
|
2216
|
+
| `SLED_FORECASTS_MINIMAL` | `list_sled_forecasts` | forecast_id, state, agency, title, estimated_advertisement_date, estimated_advertisement_raw, procurement_category, procurement_method, contract_number, incumbent_name, source_url, estimated_value(*) |
|
|
2217
|
+
| `SLED_FORECASTS_COMPREHENSIVE` | `get_sled_forecast` | Full forecast with description, contract_term, mbe_dbe_goal, delivery_location, and organization / contact / estimated_value |
|
|
2015
2218
|
|
|
2016
2219
|
All predefined shapes are validated at SDK release time (see [Developer Guide](DEVELOPERS.md#sdk-conformance-maintainers)). For custom shapes, see the [Shaping Guide](SHAPES.md).
|
|
2017
2220
|
|
|
@@ -87,6 +87,7 @@ tango webhooks list-event-types
|
|
|
87
87
|
# alerts.exclusion.match SAM.gov exclusion matched a saved alert
|
|
88
88
|
# alerts.dibbs_rfq.match DLA DIBBS RFQ matched a saved alert
|
|
89
89
|
# alerts.dibbs_rfp.match DLA DIBBS RFP matched a saved alert
|
|
90
|
+
# alerts.sled_opportunity.match State/local/education solicitation matched a saved alert
|
|
90
91
|
```
|
|
91
92
|
|
|
92
93
|
This list is served by the API, so it is always current — the SDK does not hardcode
|
|
@@ -97,11 +98,20 @@ Two things that surprise people:
|
|
|
97
98
|
|
|
98
99
|
- **DIBBS awards are not alertable.** There is no `alerts.dibbs_award.match`;
|
|
99
100
|
only RFQs and RFPs emit alerts.
|
|
100
|
-
- **Nothing fires when a record merely lapses.** An exclusion
|
|
101
|
-
termination date, or an RFQ/RFP passing its close date, emits no
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if you need to observe expiry.
|
|
101
|
+
- **Nothing fires when a record merely lapses — except SLED.** An exclusion
|
|
102
|
+
reaching its termination date, or an RFQ/RFP passing its close date, emits no
|
|
103
|
+
event: open/closed and in-force are derived at query time, so no stored row
|
|
104
|
+
changes. Poll with `list_exclusions(active=...)` /
|
|
105
|
+
`list_dibbs_rfqs(open=...)` if you need to observe expiry.
|
|
106
|
+
|
|
107
|
+
`sled_opportunity` is the exception, and the only one. A SLED solicitation's
|
|
108
|
+
liveness is a stored `status` column that Tango recomputes every fifteen
|
|
109
|
+
minutes rather than deriving per request, so a deadline passing **is** a write
|
|
110
|
+
and `alerts.sled_opportunity.match` can follow it. There is no
|
|
111
|
+
`sled_forecast` query type — a forecast has no deadline, so nothing
|
|
112
|
+
transitions — and SLED revisions and attachments are not separately
|
|
113
|
+
alertable: subscribe to `sled_opportunity` and filter on
|
|
114
|
+
`change_seen_after` or `revision_kind`.
|
|
105
115
|
|
|
106
116
|
### 2. See what a payload looks like
|
|
107
117
|
|
|
@@ -74,6 +74,8 @@ RESOURCE_TO_METHOD: dict[str, str | None] = {
|
|
|
74
74
|
"exclusions": "list_exclusions",
|
|
75
75
|
"sbir/topics": "list_sbir_topics",
|
|
76
76
|
"sbir/solicitations": "list_sbir_solicitations",
|
|
77
|
+
"sled/opportunities": "list_sled_opportunities",
|
|
78
|
+
"sled/forecasts": "list_sled_forecasts",
|
|
77
79
|
"protests": "list_protests",
|
|
78
80
|
"offices": "list_offices",
|
|
79
81
|
"psc": "list_psc",
|
|
@@ -79,6 +79,8 @@ RESOURCE_TO_MODEL: dict[str, str] = {
|
|
|
79
79
|
"exclusions": "Exclusion",
|
|
80
80
|
"sbir/topics": "SbirTopic",
|
|
81
81
|
"sbir/solicitations": "SbirSolicitation",
|
|
82
|
+
"sled/opportunities": "SledOpportunity",
|
|
83
|
+
"sled/forecasts": "SledForecast",
|
|
82
84
|
"mas_sins": "MasSin",
|
|
83
85
|
"events": "Event",
|
|
84
86
|
"news": "News",
|
|
@@ -24,6 +24,9 @@ from .models import (
|
|
|
24
24
|
SbirTopic,
|
|
25
25
|
SearchFilters,
|
|
26
26
|
ShapeConfig,
|
|
27
|
+
SledForecast,
|
|
28
|
+
SledOpportunity,
|
|
29
|
+
SledOpportunityRevision,
|
|
27
30
|
ValidateResult,
|
|
28
31
|
Vehicle,
|
|
29
32
|
VehicleMetrics,
|
|
@@ -50,7 +53,7 @@ from .webhooks import (
|
|
|
50
53
|
)
|
|
51
54
|
from .webhooks.receiver import Delivery, WebhookReceiver
|
|
52
55
|
|
|
53
|
-
__version__ = "1.
|
|
56
|
+
__version__ = "1.6.0"
|
|
54
57
|
__all__ = [
|
|
55
58
|
"TangoClient",
|
|
56
59
|
"TangoAPIError",
|
|
@@ -73,6 +76,9 @@ __all__ = [
|
|
|
73
76
|
"PaginatedResponse",
|
|
74
77
|
"SearchFilters",
|
|
75
78
|
"ShapeConfig",
|
|
79
|
+
"SledForecast",
|
|
80
|
+
"SledOpportunity",
|
|
81
|
+
"SledOpportunityRevision",
|
|
76
82
|
"ValidateResult",
|
|
77
83
|
"Vehicle",
|
|
78
84
|
"VehicleMetrics",
|