tango-python 0.6.0__tar.gz → 1.1.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.1.0/.github/workflows/docs-dispatch.yml +58 -0
- tango_python-1.1.0/.github/workflows/lint.yml +96 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/.gitignore +25 -1
- tango_python-1.1.0/CHANGELOG.md +224 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/PKG-INFO +55 -21
- {tango_python-0.6.0 → tango_python-1.1.0}/README.md +54 -20
- {tango_python-0.6.0 → tango_python-1.1.0}/docs/API_REFERENCE.md +366 -69
- tango_python-1.1.0/docs/CLIENT.md +73 -0
- tango_python-1.1.0/docs/DYNAMIC_MODELS.md +205 -0
- tango_python-1.1.0/docs/ERRORS.md +158 -0
- tango_python-1.1.0/docs/PAGINATION.md +112 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/docs/SHAPES.md +32 -28
- {tango_python-0.6.0 → tango_python-1.1.0}/docs/WEBHOOKS.md +73 -54
- {tango_python-0.6.0 → tango_python-1.1.0}/pyproject.toml +29 -1
- {tango_python-0.6.0 → tango_python-1.1.0}/scripts/check_filter_shape_conformance.py +3 -0
- tango_python-1.1.0/scripts/smoke_api_parity.py +397 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/__init__.py +19 -5
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/client.py +1385 -173
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/models.py +261 -33
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/shapes/explicit_schemas.py +171 -2
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/shapes/parser.py +60 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/webhooks/cli.py +18 -110
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/webhooks/receiver.py +9 -2
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/webhooks/signing.py +19 -6
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/webhooks/simulate.py +7 -6
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +152 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +347 -0
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination → tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +35 -35
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +97 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +87 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +175 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +1 -1
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +172 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +174 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +197 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +162 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +174 -0
- tango_python-1.1.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 +203 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +174 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +173 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +89 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +152 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +172 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +1 -1
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +81 -0
- tango_python-1.1.0/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +174 -0
- tango_python-1.1.0/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +172 -0
- tango_python-1.1.0/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +268 -0
- tango_python-1.1.0/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +252 -0
- tango_python-1.1.0/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +152 -0
- tango_python-1.1.0/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +174 -0
- tango_python-1.1.0/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +482 -0
- tango_python-1.1.0/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +252 -0
- tango_python-1.1.0/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +172 -0
- tango_python-1.1.0/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +164 -0
- tango_python-1.1.0/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +174 -0
- tango_python-1.1.0/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +162 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/production/test_production_smoke.py +4 -8
- tango_python-1.1.0/tests/test_api_parity.py +474 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/test_client.py +4 -105
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/test_shapes.py +155 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/test_webhooks_cli.py +10 -79
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/test_webhooks_receiver.py +3 -2
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/test_webhooks_signing.py +35 -13
- tango_python-0.6.0/.claude-plugin/plugin-pointer.json +0 -5
- tango_python-0.6.0/.env.example +0 -16
- tango_python-0.6.0/.github/workflows/lint.yml +0 -43
- tango_python-0.6.0/.mg-tools/DIARY.md +0 -5
- tango_python-0.6.0/.mg-tools/config.json +0 -8
- tango_python-0.6.0/.mg-tools/version +0 -1
- tango_python-0.6.0/CHANGELOG.md +0 -116
- tango_python-0.6.0/CLAUDE.md +0 -130
- tango_python-0.6.0/ROADMAP.md +0 -22
- tango_python-0.6.0/docs/DEVELOPERS.md +0 -666
- tango_python-0.6.0/docs/quick_start.ipynb +0 -941
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -76
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -86
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -86
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -86
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -86
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -81
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -86
- tango_python-0.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 -100
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -86
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -86
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -76
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -86
- tango_python-0.6.0/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -86
- tango_python-0.6.0/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -86
- tango_python-0.6.0/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -131
- tango_python-0.6.0/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -126
- tango_python-0.6.0/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -76
- tango_python-0.6.0/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -86
- tango_python-0.6.0/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -224
- tango_python-0.6.0/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -126
- tango_python-0.6.0/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -86
- tango_python-0.6.0/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -81
- tango_python-0.6.0/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -86
- tango_python-0.6.0/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -81
- tango_python-0.6.0/uv.lock +0 -2225
- {tango_python-0.6.0 → tango_python-1.1.0}/.github/workflows/publish.yml +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/.github/workflows/test.yml +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/LICENSE +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/scripts/README.md +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/scripts/fetch_api_schema.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/scripts/generate_schemas_from_api.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/scripts/pr_review.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/scripts/test_production.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/exceptions.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/shapes/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/shapes/factory.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/shapes/generator.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/shapes/models.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/shapes/schema.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/shapes/types.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tango/webhooks/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[minimal-case_id,case_number,title,source_system,outcome,filed_date] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.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-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/conftest.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/README.md +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/conftest.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_agencies_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_contracts_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_edge_cases_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_entities_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_forecasts_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_grants_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_itdashboard_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_naics_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_notices_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_offices_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_opportunities_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_organizations_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_protests_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_reference_data_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_subawards_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/integration/validation.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/production/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/production/conftest.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/test_models.py +0 -0
- {tango_python-0.6.0 → tango_python-1.1.0}/tests/test_webhooks_simulate.py +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Docs dispatch
|
|
2
|
+
|
|
3
|
+
# Fires on push to main when content that affects the published docs site
|
|
4
|
+
# changes (docs/, README, or CHANGELOG). Notifies makegov/docs via
|
|
5
|
+
# repository_dispatch so the docs site rebuilds without waiting for someone
|
|
6
|
+
# to push to the composer.
|
|
7
|
+
#
|
|
8
|
+
# tango-python is a `coloc-source` repo: its docs/ folder is the authoritative
|
|
9
|
+
# source for the Python SDK pages on docs.makegov.com (see makegov/docs#15).
|
|
10
|
+
#
|
|
11
|
+
# Required secrets:
|
|
12
|
+
# DOCS_DISPATCH_TOKEN — GitHub token with contents:write on makegov/docs.
|
|
13
|
+
#
|
|
14
|
+
# Required variables (optional):
|
|
15
|
+
# DOCS_TARGET_REPO — override the dispatch target (default: makegov/docs).
|
|
16
|
+
|
|
17
|
+
on:
|
|
18
|
+
push:
|
|
19
|
+
branches:
|
|
20
|
+
- main
|
|
21
|
+
paths:
|
|
22
|
+
- "docs/**"
|
|
23
|
+
- "README.md"
|
|
24
|
+
- "CHANGELOG.md"
|
|
25
|
+
workflow_dispatch:
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
dispatch:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
fetch-depth: 2
|
|
36
|
+
|
|
37
|
+
- name: Detect changed paths
|
|
38
|
+
id: changes
|
|
39
|
+
run: |
|
|
40
|
+
set -euo pipefail
|
|
41
|
+
base=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD)
|
|
42
|
+
external=$(git diff --name-only "$base" HEAD -- 'docs' 'README.md' 'CHANGELOG.md' | paste -sd, -)
|
|
43
|
+
{
|
|
44
|
+
echo "external=$external"
|
|
45
|
+
echo "has_external=$([ -n "$external" ] && echo true || echo false)"
|
|
46
|
+
} >> "$GITHUB_OUTPUT"
|
|
47
|
+
|
|
48
|
+
- name: Dispatch to docs composer (makegov/docs)
|
|
49
|
+
if: steps.changes.outputs.has_external == 'true'
|
|
50
|
+
env:
|
|
51
|
+
GH_TOKEN: ${{ secrets.DOCS_DISPATCH_TOKEN }}
|
|
52
|
+
TARGET: ${{ vars.DOCS_TARGET_REPO || 'makegov/docs' }}
|
|
53
|
+
run: |
|
|
54
|
+
gh api "repos/$TARGET/dispatches" \
|
|
55
|
+
-f event_type=external_updated \
|
|
56
|
+
-f "client_payload[source_repo]=${{ github.repository }}" \
|
|
57
|
+
-f "client_payload[source_ref]=${{ github.sha }}" \
|
|
58
|
+
-f "client_payload[changed_paths]=${{ steps.changes.outputs.external }}"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: Linting
|
|
2
|
+
|
|
3
|
+
# Lint gate runs on every PR and push to main.
|
|
4
|
+
#
|
|
5
|
+
# - ruff format + ruff check are HARD gates (block the PR).
|
|
6
|
+
# - mypy is ADVISORY for now (continue-on-error): the package carries ~28
|
|
7
|
+
# pre-existing type errors that predate CI enforcement. Tracked for burn-down
|
|
8
|
+
# in makegov/tango-python; flip `continue-on-error` off once that's clear.
|
|
9
|
+
# - The SDK filter/shape conformance check needs the canonical manifest from the
|
|
10
|
+
# private makegov/tango repo, which requires a TANGO_API_REPO_ACCESS_TOKEN
|
|
11
|
+
# secret the public CI does not have. The conformance job SKIPS cleanly when
|
|
12
|
+
# the token is absent (rather than failing red) and becomes a real gate the
|
|
13
|
+
# moment the secret is configured.
|
|
14
|
+
on:
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
push:
|
|
17
|
+
branches: [ main ]
|
|
18
|
+
pull_request:
|
|
19
|
+
branches: [ main ]
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
lint:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v4
|
|
30
|
+
with:
|
|
31
|
+
version: "latest"
|
|
32
|
+
|
|
33
|
+
- name: Set up Python
|
|
34
|
+
run: uv python install 3.12
|
|
35
|
+
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: uv sync --all-extras
|
|
38
|
+
|
|
39
|
+
- name: Check formatting with ruff
|
|
40
|
+
run: uv run ruff format --check tango/
|
|
41
|
+
|
|
42
|
+
- name: Lint with ruff
|
|
43
|
+
run: uv run ruff check tango/
|
|
44
|
+
|
|
45
|
+
- name: Type check with mypy (advisory)
|
|
46
|
+
continue-on-error: true
|
|
47
|
+
run: uv run mypy tango/
|
|
48
|
+
|
|
49
|
+
conformance:
|
|
50
|
+
# Requires the canonical filter_shape manifest from the private makegov/tango
|
|
51
|
+
# repo. When TANGO_API_REPO_ACCESS_TOKEN is not configured, every real step
|
|
52
|
+
# is skipped and the job passes (rather than failing on an empty token).
|
|
53
|
+
# Configure the secret to turn this into a hard gate automatically.
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
|
|
56
|
+
steps:
|
|
57
|
+
- name: Determine token availability
|
|
58
|
+
id: gate
|
|
59
|
+
env:
|
|
60
|
+
TANGO_API_REPO_ACCESS_TOKEN: ${{ secrets.TANGO_API_REPO_ACCESS_TOKEN }}
|
|
61
|
+
run: |
|
|
62
|
+
if [ -n "$TANGO_API_REPO_ACCESS_TOKEN" ]; then
|
|
63
|
+
echo "ready=true" >> "$GITHUB_OUTPUT"
|
|
64
|
+
else
|
|
65
|
+
echo "ready=false" >> "$GITHUB_OUTPUT"
|
|
66
|
+
echo "::notice::Skipping SDK conformance check — TANGO_API_REPO_ACCESS_TOKEN not configured."
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
- uses: actions/checkout@v4
|
|
70
|
+
if: steps.gate.outputs.ready == 'true'
|
|
71
|
+
|
|
72
|
+
- name: Checkout tango API repo (manifest source)
|
|
73
|
+
if: steps.gate.outputs.ready == 'true'
|
|
74
|
+
uses: actions/checkout@v4
|
|
75
|
+
with:
|
|
76
|
+
repository: makegov/tango
|
|
77
|
+
path: tango-api
|
|
78
|
+
token: ${{ secrets.TANGO_API_REPO_ACCESS_TOKEN }}
|
|
79
|
+
|
|
80
|
+
- name: Install uv
|
|
81
|
+
if: steps.gate.outputs.ready == 'true'
|
|
82
|
+
uses: astral-sh/setup-uv@v4
|
|
83
|
+
with:
|
|
84
|
+
version: "latest"
|
|
85
|
+
|
|
86
|
+
- name: Set up Python
|
|
87
|
+
if: steps.gate.outputs.ready == 'true'
|
|
88
|
+
run: uv python install 3.12
|
|
89
|
+
|
|
90
|
+
- name: Install dependencies
|
|
91
|
+
if: steps.gate.outputs.ready == 'true'
|
|
92
|
+
run: uv sync --all-extras
|
|
93
|
+
|
|
94
|
+
- name: Check SDK filter/shape conformance
|
|
95
|
+
if: steps.gate.outputs.ready == 'true'
|
|
96
|
+
run: uv run python scripts/check_filter_shape_conformance.py --manifest tango-api/contracts/filter_shape_contract.json
|
|
@@ -149,4 +149,28 @@ Thumbs.db
|
|
|
149
149
|
yoni/
|
|
150
150
|
.cursor/*
|
|
151
151
|
|
|
152
|
-
examples/
|
|
152
|
+
examples/
|
|
153
|
+
|
|
154
|
+
.claude-plugin/
|
|
155
|
+
CLAUDE.md
|
|
156
|
+
# >>> mg-tools >>>
|
|
157
|
+
# --- mg-tools (per-developer; re-run 'mg-tools install' after clone) ---
|
|
158
|
+
.claude-plugin/plugin-pointer.json
|
|
159
|
+
.claude/settings.json
|
|
160
|
+
.claude/settings.local.json
|
|
161
|
+
.claude/mg-tools-integration.md
|
|
162
|
+
.mcp.json
|
|
163
|
+
.mcp.disabled.json
|
|
164
|
+
.mg-tools/
|
|
165
|
+
.claude/worktrees/
|
|
166
|
+
.claude/scheduled_tasks.lock
|
|
167
|
+
.claude.bak.*
|
|
168
|
+
.cursor.bak.*
|
|
169
|
+
.mcp.json.bak.*
|
|
170
|
+
# Local IDE/editor config files
|
|
171
|
+
.vscode/
|
|
172
|
+
*.code-workspace
|
|
173
|
+
.zed/
|
|
174
|
+
.idea/
|
|
175
|
+
# <<< mg-tools <<<
|
|
176
|
+
>>>>>>> Stashed changes
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.1.0] - 2026-05-29
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `_parse_webhook_alert`: aligned the parser output with the `WebhookAlert`
|
|
14
|
+
type contract. Sparse server payloads now hydrate `query_type` and `filters`
|
|
15
|
+
as `""` / `{}` (matching their declared non-null types) rather than `None`,
|
|
16
|
+
and `status` is typed as the `Literal["active", "paused"]` the model promises.
|
|
17
|
+
Clears the four type-check errors this introduced; no change for full payloads.
|
|
18
|
+
- `Contract` model: removed dead fields (`id`, `award_id`, `recipient_name`,
|
|
19
|
+
`award_amount`, `awarding_agency`, `funding_agency`) and added the real API
|
|
20
|
+
fields (`key`, `piid`, `obligated`, `total_contract_value`,
|
|
21
|
+
`base_and_exercised_options_value`, `awarding_office`, `funding_office`,
|
|
22
|
+
`naics_code`, `psc_code`, `set_aside`, `solicitation_identifier`,
|
|
23
|
+
`parent_award`, `legislative_mandates`, `subawards_summary`,
|
|
24
|
+
`place_of_performance`). The deprecated fields remain declared with `None`
|
|
25
|
+
defaults for one minor cycle (they never returned data) and will be removed
|
|
26
|
+
in `2.0.0`. New `OrganizationOfficePayload` dataclass for the office fields.
|
|
27
|
+
(`Contract` is a documentation-only dataclass — not instantiated or exported
|
|
28
|
+
— so there is no runtime impact.)
|
|
29
|
+
- `list_contracts`: no longer sends `page=1` to the cursor-only `/api/contracts/`
|
|
30
|
+
endpoint. When no cursor is supplied, neither `page` nor `cursor` is sent and
|
|
31
|
+
the API returns the first page by default.
|
|
32
|
+
- Shape validation: registered the `ContractOrIDVCompetition` nested schema
|
|
33
|
+
(alias of `Competition`) so nested selections like
|
|
34
|
+
`competition(extent_competed,number_of_offers_received)` on contract / IDV
|
|
35
|
+
shapes validate instead of raising `ShapeValidationError`.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
- Budget accounts surface (tango v4.6.8): `list_budget_accounts`,
|
|
39
|
+
`get_budget_account`, `get_budget_account_quarters`,
|
|
40
|
+
`get_budget_account_recipients`. New `BudgetAccount` dataclass exported from
|
|
41
|
+
the top-level package, plus `ShapeConfig.BUDGET_ACCOUNTS_MINIMAL`.
|
|
42
|
+
- Singleton detail GETs: `get_contract`, `get_contract_subawards`,
|
|
43
|
+
`get_contract_transactions`, `get_forecast`, `get_grant`, `get_notice`,
|
|
44
|
+
`get_opportunity`, `get_subaward`.
|
|
45
|
+
- `get_entity_budget_flows(uei)` for `/api/entities/{uei}/budget-flows/`.
|
|
46
|
+
- `list_otidv_awards(key)` for `/api/otidvs/{key}/awards/` (parity with Node).
|
|
47
|
+
- `grant_id` filter kwarg on `list_grants` (supports multi-value OR via `|`).
|
|
48
|
+
|
|
49
|
+
### CI
|
|
50
|
+
- Re-enabled `lint.yml` as a PR + push-to-main gate. `ruff format` and
|
|
51
|
+
`ruff check` are hard gates; `mypy` runs advisory (continue-on-error) pending
|
|
52
|
+
burn-down of ~28 pre-existing type errors. The filter/shape conformance check
|
|
53
|
+
is a separate job that skips cleanly until a `TANGO_API_REPO_ACCESS_TOKEN`
|
|
54
|
+
secret for the private manifest repo is configured, at which point it becomes
|
|
55
|
+
a hard gate.
|
|
56
|
+
|
|
57
|
+
## [1.0.0] - 2026-05-13
|
|
58
|
+
|
|
59
|
+
> First stable release. `tango-python` is now at full API parity with the
|
|
60
|
+
> Tango HTTP surface, the legacy subject-based webhook subscription
|
|
61
|
+
> mechanism has been removed in favor of filter alerts, the shape parser
|
|
62
|
+
> agrees byte-for-byte with the server's expand-alias handling, and the
|
|
63
|
+
> SDK's docs are auto-published to `docs.makegov.com/sdks/python/` via the
|
|
64
|
+
> composer pipeline (makegov/docs#15 / makegov/docs#16). From `1.x` on,
|
|
65
|
+
> we'll only do breaking changes on a major bump.
|
|
66
|
+
>
|
|
67
|
+
> Originally tracked as: API parity (PR #25), subject-based webhook
|
|
68
|
+
> removal (PR #27 / issue #2275), shape-validator alias support (PR #28 /
|
|
69
|
+
> issue #2266), and the docs-only content port (makegov/docs#16).
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
- `ordering` parameter on `list_forecasts`, `list_grants`, `list_subawards`, `list_gsa_elibrary_contracts`, and `list_opportunities`. Prefix with `-` for descending. Closes a parity gap with the API surface (these endpoints all accept `?ordering=` server-side).
|
|
73
|
+
- `create_webhook_endpoint` accepts `name=` (keyword-only) and now **requires** it. The Tango API enforces unique `(user, name)` on endpoints; omitting `name` returns a 400 server-side, so the SDK raises `TangoValidationError` client-side instead of round-tripping. (0.7.0 — never publicly released — emitted a `DeprecationWarning` instead.)
|
|
74
|
+
- `update_webhook_endpoint` accepts `name=` for renaming an endpoint.
|
|
75
|
+
- Webhook alerts (filter subscriptions): `list_webhook_alerts`, `get_webhook_alert`, `create_webhook_alert`, `update_webhook_alert`, `delete_webhook_alert` — the canonical write surface over `/api/webhooks/alerts/`. New `WebhookAlert` dataclass exported from the top-level package.
|
|
76
|
+
- `resolve(name, target_type, ...)` — POST `/api/resolve/` to rank entity / organization candidates from a free-text name. Returns `ResolveResult` with `ResolveCandidate` entries (both exported).
|
|
77
|
+
- `validate(identifier_type, value)` — POST `/api/validate/` to validate the format of a PIID, solicitation number, or UEI. Returns `ValidateResult` (exported).
|
|
78
|
+
- Reference data: `list_departments`, `get_department`, `list_psc`, `get_psc`, `get_psc_metrics`, `get_naics`, `get_naics_metrics`, `get_business_type`, `list_assistance_listings`, `get_assistance_listing`, `list_mas_sins`, `get_mas_sin`.
|
|
79
|
+
- Entity sub-resources: `list_entity_contracts`, `list_entity_idvs`, `list_entity_otas`, `list_entity_otidvs`, `list_entity_subawards`, `list_entity_lcats`, `get_entity_metrics`. All shape-aware where the underlying endpoint supports shaping.
|
|
80
|
+
- IDV sub-resources: `list_idv_lcats`.
|
|
81
|
+
- Agency sub-resources: `list_agency_awarding_contracts`, `list_agency_funding_contracts`.
|
|
82
|
+
- Misc: `search_opportunity_attachments(q, top_k, include_extracted_text)` for `/api/opportunities/attachment-search/`; `get_version()` for `/api/version/`; `list_api_keys()` for `/api/api-keys/`.
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
- `create_webhook_alert` accepts `endpoint=` (keyword-only). Required for accounts with multiple webhook endpoints; auto-resolves for single-endpoint accounts. Closes the multi-endpoint smoke-test gap (tango#2256).
|
|
86
|
+
- `test_webhook_delivery` now sends the canonical `endpoint` body key instead of the deprecated `endpoint_id` alias (tango#2252). The Python kwarg name stays `endpoint_id=` for backwards compatibility; the wire payload is what changed.
|
|
87
|
+
- **`generate_signature(body, secret)` now returns the full wire form `"sha256=<hex>"`** instead of bare hex. Callers can assign the return value directly to the `X-Tango-Signature` header without wrapping in a format string. This is a breaking change for code that relied on the bare-hex return; pass it through `parse_signature_header()` to recover the previous form. `verify_signature` accepts both prefixed and bare-hex inputs (unchanged), so receivers continue to work either way.
|
|
88
|
+
|
|
89
|
+
### Removed
|
|
90
|
+
- **Subject-based webhook subscription surface** (tango#2275). Migrate to `create_webhook_alert(...)` and the alerts API.
|
|
91
|
+
- Methods: `list_webhook_subscriptions`, `get_webhook_subscription`, `create_webhook_subscription`, `update_webhook_subscription`, `delete_webhook_subscription`.
|
|
92
|
+
- Dataclasses: `WebhookSubscription`, `WebhookSubjectTypeDefinition`. Both are no longer exported from the top-level `tango` package — importing them raises `ImportError`.
|
|
93
|
+
- Fields: `default_subject_type` removed from `WebhookEventType`; `subject_types` and `subject_type_definitions` removed from `WebhookEventTypesResponse`. The server's `/api/webhooks/event-types/` response no longer carries these.
|
|
94
|
+
- CLI: the entire `tango webhooks subscriptions` Click subgroup (`list` / `get` / `create` / `delete`). Use the SDK's `client.create_webhook_alert(...)` etc. directly — there is no CLI subgroup for alerts.
|
|
95
|
+
- `ordering` kwarg from `list_notices` and `list_protests`. The notices and protests viewsets reject every `?ordering=` value at runtime (tango#2254); the kwarg silently sent unsupported values. Other five list methods retain `ordering`.
|
|
96
|
+
|
|
97
|
+
### Fixed
|
|
98
|
+
- `TangoClient._post()` and `_patch()` accept both `json_data=` (positional) and `json=` (keyword) for backward compatibility. Internal callers and docs examples that use `json=` no longer fail with `TypeError`. Passing **both** now raises `TangoValidationError` rather than silently preferring one — that ambiguity would hide caller bugs.
|
|
99
|
+
- `get_psc_metrics` / `get_naics_metrics` / `get_entity_metrics` docstrings — `period_grouping` values are `"month"` / `"quarter"` / `"year"` (the path-segment values the API accepts), not `"monthly"` / `"quarterly"`.
|
|
100
|
+
- `docs/API_REFERENCE.md#get_agency` — example uses `client.get_agency("GSA")` consistently and notes the parameter accepts CGAC / FPDS / short code / abbreviation / canonical name.
|
|
101
|
+
- `README.md` Quick Start — `get_agency()` returns an `Agency` dataclass, so the example uses attribute access (`agency.name`) instead of `agency['name']` which would `TypeError`.
|
|
102
|
+
- `scripts/smoke_api_parity.py` — `list_business_types(limit=1)` is now wrapped in the `run(...)` helper so a failure on that call records FAIL instead of aborting the smoke run.
|
|
103
|
+
- `tango webhooks endpoints create` CLI now accepts and requires `--name` (passed through to `create_webhook_endpoint(name=...)`). Previously the option was absent, meaning the CLI could never set a custom endpoint name and every call would 400 server-side (the server enforces `unique(user, name)`).
|
|
104
|
+
- `WebhookAlert.query_type` and `WebhookAlert.filters` tightened from `Optional` to non-optional (`str` and `dict[str, Any]` respectively). Legacy nullable rows were purged by the tango#2275 migration; the server model and serializer guarantee non-null values for all current data. `WebhookAlert.status` narrowed from `str` to `Literal["active", "paused"]` — the server serializer produces exactly those two values.
|
|
105
|
+
- **Shape validator agrees with server on `naics(...)` / `psc(...)` expansions.** The client-side `ShapeParser.validate()` previously rejected the canonical `shape=naics(code,description)` form (which the server has always accepted) and also rejected the alias `shape=naics_code(code,description)`. The parser now mirrors the server's `_EXPAND_ALIASES` (introduced in Tango PR makegov/tango#2259) and rewrites `naics_code(...)` / `psc_code(...)` to their canonical `naics(...)` / `psc(...)` form at parse time. Bare scalar leaves (`shape=naics_code` / `shape=psc_code`) are left untouched and still return the raw column value, matching the server. Schemas for `Contract`, `Forecast`, `Opportunity`, `Notice`, and `Vehicle` gained explicit `naics` / `psc` expand entries backed by the existing `CodeDescription` nested model. Fixes makegov/tango#2266.
|
|
106
|
+
- **`Subaward` schema matches the server's `SubawardSerializer`.** The previous `SUBAWARD_SCHEMA` declared two fields the server has never exposed (`id`, `amount`) and was missing every real field on the resource — including `piid`, `key`, `awarding_office` / `funding_office` / `place_of_performance` / `subaward_details` / `fsrs_details` / `highly_compensated_officers` / `usaspending_permalink`, and the denormalized `prime_awardee_*` / `recipient_*` lookup columns. Shape strings that referenced any real field (e.g. `shape="piid"`) would fail client-side validation with `unknown_field`, and conversely the SDK happily passed `shape="id"` / `shape="amount"` through to the server, where they were rejected. `SUBAWARD_SCHEMA` is now derived directly from `awards.serializers.subawards.SubawardSerializer` and the resource's runtime `available_fields`. The `Subaward` dataclass in `tango/models.py` was updated to match. New nested schemas `SubawardDetails`, `FsrsDetails`, `SubawardPlaceOfPerformance`, and `HighlyCompensatedOfficer` are registered so the corresponding shape expansions validate end-to-end.
|
|
107
|
+
|
|
108
|
+
### Documentation
|
|
109
|
+
- New `docs/ERRORS.md` — full exception hierarchy, recovery patterns, and the shape-error classes (`ShapeValidationError`, `ShapeParseError`, `TypeGenerationError`, `ModelInstantiationError`). Ported from `docs.makegov.com/sdks/python/errors.md` ahead of the docs-site auto-pull cutover (makegov/docs#15 / makegov/docs#16).
|
|
110
|
+
- New `docs/PAGINATION.md` — page-based vs cursor-based strategies, iteration patterns, and the `PaginatedResponse` field reference. Ported from `docs.makegov.com/sdks/python/pagination.md`.
|
|
111
|
+
- New `docs/CLIENT.md` — `TangoClient` constructor reference, `rate_limit_info` / `last_response_headers` properties, and retry-semantics note (the SDK has no built-in retry). Ported from `docs.makegov.com/sdks/python/client.md`.
|
|
112
|
+
|
|
113
|
+
### CI
|
|
114
|
+
- New `.github/workflows/docs-dispatch.yml` — fires on push to `main` when `docs/**`, `README.md`, or `CHANGELOG.md` changes and dispatches `external_updated` at `makegov/docs` so the public docs site rebuilds with the latest SDK content. Required for the makegov/docs#15 auto-pull pipeline.
|
|
115
|
+
|
|
116
|
+
## [0.6.0] - 2026-05-07
|
|
117
|
+
|
|
118
|
+
### Added
|
|
119
|
+
- Vehicles: new top-level fields `program_acronym`, `idv_count`, `total_obligated`, `is_synthetic_solicitation`, `latest_award_date`, `description`, `opportunity_id`.
|
|
120
|
+
- Vehicles: new `metrics(*)` shape expansion bundling 12 computed metrics: `avg_offers_received`, `award_concentration_hhi`, `order_concentration_hhi`, `competed_rate`, `using_agency_count`, `avg_order_value`, `max_order_value`, `top_recipient_share`, `recent_obligations_24mo`, `recent_orders_24mo`, `days_since_last_order`, `obligation_to_ceiling_ratio`. Backed by a new `VehicleMetrics` schema.
|
|
121
|
+
- `list_vehicle_orders(uuid, ...)` for the new `/api/vehicles/{uuid}/orders/` endpoint, returning task orders under the vehicle's IDVs with two-phase pagination.
|
|
122
|
+
- `list_vehicles` gained 21 explicit filter parameters per API 4.3.0: `vehicle_type`, `type_of_idc`, `contract_type`, `set_aside` (multi-value via `|`), `who_can_use`, `naics_code`, `psc_code`, `program_acronym`, `agency`, `organization_id`, `total_obligated_min`/`max`, `idv_count_min`/`max`, `order_count_min`/`max`, `fiscal_year`, `award_date_after`/`before`, `last_date_to_order_after`/`before`.
|
|
123
|
+
- `list_vehicle_awardees` gained a `search` parameter for entity-aware full-text search across IDV fields and recipient entity details (API 4.3.0).
|
|
124
|
+
- `ordering` parameter on `list_vehicles` (whitelist: `vehicle_obligations`, `latest_award_date`, `total_obligated`, `award_date`, `last_date_to_order`, `fiscal_year`, `idv_count`, `order_count`) and on `list_vehicle_orders` (whitelist: `award_date`, `obligated`, `total_contract_value`). Prefix with `-` for descending.
|
|
125
|
+
- `ShapeConfig.VEHICLE_ORDERS_MINIMAL` default for the new orders endpoint.
|
|
126
|
+
- Shaping: New `organization(*)` expand on `Vehicle`, `Forecast`, `Grant`, `ITDashboardInvestment`, and `Protest` schemas — returns the canonical 7-key office payload (`organization_id`, `office_code`, `office_name`, `agency_code`, `agency_name`, `department_code`, `department_name`). Selectable as the bare leaf (`shape=...,organization`) or as a sub-selectable expansion (`shape=...,organization(office_code,...)`).
|
|
127
|
+
- Shaping: New `vehicle(*)` expand on `Contract` — request the parent vehicle inline from `/api/contracts/` (API 4.2.0).
|
|
128
|
+
- `Vehicle` and `VehicleMetrics` are now exported from the top-level `tango` package.
|
|
129
|
+
- `tango.webhooks` subpackage with HMAC-SHA256 signing helpers (`verify_signature`, `generate_signature`, `parse_signature_header`) that mirror the canonical Tango server scheme byte-for-byte. Importable from a default `pip install tango-python` (pure stdlib).
|
|
130
|
+
- `WebhookReceiver`: a stdlib-based local HTTP listener for development and integration tests. Verifies signatures, optionally forwards each delivery to a downstream URL, and records deliveries in memory for inspection. Usable as a context manager (`with WebhookReceiver(secret=...).run() as rx: ...`).
|
|
131
|
+
- `tango.webhooks.simulate.deliver(...)`: locally sign and POST a payload to any URL — no Tango involvement. Useful for offline iteration on receiver code.
|
|
132
|
+
- New `tango[webhooks]` extra (adds `click`) ships a `tango` console script covering the full webhook lifecycle for developer integrations:
|
|
133
|
+
- `listen` — local receiver
|
|
134
|
+
- `simulate` — sign a payload locally; with `--to`, also POST it
|
|
135
|
+
- `trigger` — ask Tango to send a real test delivery
|
|
136
|
+
- `fetch-sample` — print the canonical payload Tango emits for an event type
|
|
137
|
+
- `list-event-types` — discover what's subscribable
|
|
138
|
+
- `endpoints list|get|create|delete` — manage delivery endpoints
|
|
139
|
+
- `subscriptions list|get|create|delete` — manage what events you receive
|
|
140
|
+
Together these let a developer go from zero to receiving real Tango webhooks without leaving the shell or dropping into Python.
|
|
141
|
+
|
|
142
|
+
### Changed
|
|
143
|
+
- `ShapeConfig.VEHICLES_MINIMAL` and `VEHICLES_COMPREHENSIVE` now include the new top-level fields and the `organization` expansion. `VEHICLES_COMPREHENSIVE` defaults to `metrics(*)` and no longer pulls the deprecated `competition_details(*)` blob.
|
|
144
|
+
|
|
145
|
+
### Deprecated
|
|
146
|
+
- Vehicles shape fields `agency_details`, `competition_details`, and the `opportunity` expansion. The upstream API now sends a `Deprecation: true` header for these and recomputes them at request time. Explicit use in `shape=...` emits a Python `DeprecationWarning`. Sunset timeline TBD upstream.
|
|
147
|
+
|
|
148
|
+
### Notes
|
|
149
|
+
- Console script name `tango` may be revisited in a future release if it conflicts with sibling tooling (`tango-scripts` reuses the bare name).
|
|
150
|
+
|
|
151
|
+
### Documentation
|
|
152
|
+
- New `docs/WEBHOOKS.md` — comprehensive guide covering install, concepts, a zero-to-receiving quickstart, full CLI reference, and programmatic patterns for `WebhookReceiver` / `simulate.sign` / `simulate.deliver` in pytest fixtures.
|
|
153
|
+
- `docs/API_REFERENCE.md`: filled in `get_webhook_subscription`, replaced the hand-rolled signature-verification snippet with a pointer to `tango.webhooks.verify_signature`, and added a new "Webhook tooling (`tango.webhooks`)" section that documents every importable from the new subpackage.
|
|
154
|
+
- `README.md`: new "Webhook Tooling" section under Advanced Features, plus the new guide is linked from the Documentation index.
|
|
155
|
+
|
|
156
|
+
## [0.5.0] - 2026-04-08
|
|
157
|
+
|
|
158
|
+
### Added
|
|
159
|
+
- 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.
|
|
160
|
+
|
|
161
|
+
## [0.4.4] - 2026-03-25
|
|
162
|
+
|
|
163
|
+
### Added
|
|
164
|
+
- `parent_piid` filter parameter on `list_contracts` for filtering orders under a specific parent IDV PIID.
|
|
165
|
+
- `user_agent` and `extra_headers` parameters on `TangoClient` for custom request headers.
|
|
166
|
+
- `TangoClient.last_response_headers` property for accessing full HTTP headers from the most recent API response.
|
|
167
|
+
|
|
168
|
+
## [0.4.3] - 2026-03-21
|
|
169
|
+
|
|
170
|
+
### Added
|
|
171
|
+
- `TangoRateLimitError` now exposes `wait_in_seconds`, `detail`, and `limit_type` properties parsed from the API's 429 response body.
|
|
172
|
+
- `RateLimitInfo` dataclass for structured access to rate limit headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and per-window daily/burst variants).
|
|
173
|
+
- `TangoClient.rate_limit_info` property returns rate limit info from the most recent API response.
|
|
174
|
+
|
|
175
|
+
### Changed
|
|
176
|
+
- `_request` now passes the full 429 response body to `TangoRateLimitError` (previously discarded), enabling callers to access `wait_in_seconds` and the specific limit type that was exceeded.
|
|
177
|
+
|
|
178
|
+
## [0.4.2] - 2026-03-04
|
|
179
|
+
|
|
180
|
+
### Added
|
|
181
|
+
- Protests endpoints: `list_protests`, `get_protest` with shaping and filter params (`source_system`, `outcome`, `case_type`, `agency`, `case_number`, `solicitation_number`, `protester`, `filed_date_after`, `filed_date_before`, `decision_date_after`, `decision_date_before`, `search`).
|
|
182
|
+
|
|
183
|
+
### Changed
|
|
184
|
+
- Lint CI workflow disabled for push/PR (runs only on manual trigger) until the private `makegov/tango` repo is accessible to the workflow.
|
|
185
|
+
- Updated documents to reflect changes since v0.4.0
|
|
186
|
+
- Entities: `ENTITIES_COMPREHENSIVE` now uses `federal_obligations(*)` expansion; the API treats federal obligations as an expansion rather than a plain shape field.
|
|
187
|
+
- Docs: `SHAPES.md` documents `federal_obligations(*)` as an expansion for entity shaping.
|
|
188
|
+
- Integration tests: `test_parsing_nested_objects_with_missing_data` accepts award office fields (`office_code`, `agency_code`, `department_code`) and empty nested objects when the API returns partial data.
|
|
189
|
+
|
|
190
|
+
### Removed
|
|
191
|
+
- Assistance: `list_assistance` endpoint and all related tests, docs, and references.
|
|
192
|
+
- IDV summaries: `get_idv_summary` and `list_idv_summary_awards` endpoints and related integration tests, cassettes, and API reference section.
|
|
193
|
+
|
|
194
|
+
## [0.4.1] - 2026-03-03
|
|
195
|
+
|
|
196
|
+
### Added
|
|
197
|
+
- GSA eLibrary contracts: `list_gsa_elibrary_contracts`, `get_gsa_elibrary_contract` with shaping and filter params (`contract_number`, `key`, `piid`, `schedule`, `search`, `sin`, `uei`).
|
|
198
|
+
|
|
199
|
+
### Changed
|
|
200
|
+
- Conformance: replaced `**kwargs`/`**filters` with explicit filter parameters on `list_contracts`, `list_idvs`, `list_entities`, `list_forecasts`, `list_grants`, `list_notices`, `list_opportunities` for full filter/shape conformance. Backward compatibility preserved for `list_contracts(filters=SearchFilters(...))`.
|
|
201
|
+
|
|
202
|
+
## [0.4.0] - 2026-02-24
|
|
203
|
+
|
|
204
|
+
### Added
|
|
205
|
+
- Offices, Organizations, OTAs, OTIDVs, Subawards, NAICS, and Assistance endpoints.
|
|
206
|
+
- Filter/shape conformance tooling and documentation.
|
|
207
|
+
|
|
208
|
+
### Changed
|
|
209
|
+
- CI lint workflow runs filter/shape conformance when the manifest is available.
|
|
210
|
+
|
|
211
|
+
## [0.3.0] - 2026-02-09
|
|
212
|
+
|
|
213
|
+
### Added
|
|
214
|
+
- Vehicles endpoints: `list_vehicles`, `get_vehicle`, and `list_vehicle_awardees` (supports shaping + flattening). (refs `makegov/tango#1328`)
|
|
215
|
+
- IDV endpoints: `list_idvs`, `get_idv`, `list_idv_awards`, `list_idv_child_idvs`, `list_idv_transactions`, `get_idv_summary`, `list_idv_summary_awards`. (refs `makegov/tango#1328`)
|
|
216
|
+
- Webhooks v2 client support: event type discovery, subscription CRUD, endpoint management, test delivery, and sample payload helpers. (refs `makegov/tango#1274`)
|
|
217
|
+
|
|
218
|
+
### Changed
|
|
219
|
+
- Expanded explicit schemas to support common IDV shaping expansions (award offices, officers, period of performance, etc.).
|
|
220
|
+
- HTTP client now supports PATCH/DELETE helpers for webhook management endpoints.
|
|
221
|
+
|
|
222
|
+
## [0.2.0] - 2025-11-16
|
|
223
|
+
|
|
224
|
+
- Entirely refactored SDK
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tango-python
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.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
|
|
@@ -57,6 +57,10 @@ Description-Content-Type: text/markdown
|
|
|
57
57
|
|
|
58
58
|
# Tango Python SDK
|
|
59
59
|
|
|
60
|
+
[](https://pypi.org/project/tango-python/)
|
|
61
|
+
[](https://pypi.org/project/tango-python/)
|
|
62
|
+
[](LICENSE)
|
|
63
|
+
|
|
60
64
|
A modern Python SDK for the [Tango API](https://tango.makegov.com) by MakeGov, featuring dynamic response shaping and comprehensive type hints.
|
|
61
65
|
|
|
62
66
|
## Features
|
|
@@ -96,12 +100,14 @@ print(f"Found {agencies.count} agencies")
|
|
|
96
100
|
|
|
97
101
|
# Get specific agency
|
|
98
102
|
agency = client.get_agency("GSA")
|
|
99
|
-
print(f"Agency: {agency
|
|
103
|
+
print(f"Agency: {agency.name}")
|
|
100
104
|
|
|
101
105
|
# Search contracts
|
|
102
106
|
contracts = client.list_contracts(
|
|
103
107
|
limit=10
|
|
104
108
|
)
|
|
109
|
+
```
|
|
110
|
+
|
|
105
111
|
## Authentication
|
|
106
112
|
|
|
107
113
|
Most endpoints require an API key. You can obtain one from the [Tango API portal](https://tango.makegov.com).
|
|
@@ -261,13 +267,13 @@ opportunities = client.list_opportunities(agency="DOD", active=True, limit=25)
|
|
|
261
267
|
### Notices
|
|
262
268
|
|
|
263
269
|
```python
|
|
264
|
-
notices = client.list_notices(agency="DOD", notice_type="
|
|
270
|
+
notices = client.list_notices(agency="DOD", notice_type="Presolicitation", limit=25)
|
|
265
271
|
```
|
|
266
272
|
|
|
267
273
|
### Grants
|
|
268
274
|
|
|
269
275
|
```python
|
|
270
|
-
grants = client.list_grants(agency="HHS", status="
|
|
276
|
+
grants = client.list_grants(agency="HHS", status="F", limit=25) # F = Forecasted
|
|
271
277
|
```
|
|
272
278
|
|
|
273
279
|
### Protests
|
|
@@ -287,12 +293,45 @@ contract = client.get_gsa_elibrary_contract("UUID")
|
|
|
287
293
|
### Reference Data
|
|
288
294
|
|
|
289
295
|
```python
|
|
290
|
-
# Offices, organizations, NAICS, subawards, business types
|
|
296
|
+
# Offices, organizations, NAICS, PSC, subawards, business types
|
|
291
297
|
offices = client.list_offices(search="acquisitions")
|
|
292
298
|
organizations = client.list_organizations(level=1)
|
|
293
299
|
naics = client.list_naics(search="software")
|
|
300
|
+
get_naics = client.get_naics("541511")
|
|
301
|
+
psc = client.list_psc()
|
|
294
302
|
subawards = client.list_subawards(prime_uei="UEI123")
|
|
295
303
|
business_types = client.list_business_types()
|
|
304
|
+
mas_sins = client.list_mas_sins()
|
|
305
|
+
assistance = client.list_assistance_listings()
|
|
306
|
+
departments = client.list_departments()
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Resolve / Validate
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
# Resolve a name to entity/org candidates
|
|
313
|
+
result = client.resolve(name="Lockheed Martin", target_type="entity")
|
|
314
|
+
for c in result.candidates:
|
|
315
|
+
print(c.identifier, c.display_name)
|
|
316
|
+
|
|
317
|
+
# Validate an identifier
|
|
318
|
+
result = client.validate(identifier_type="uei", value="ABCDEF123456")
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### IT Dashboard
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
investments = client.list_itdashboard_investments(search="cloud", limit=25)
|
|
325
|
+
investment = client.get_itdashboard_investment("023-000001234")
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Entity Sub-resources
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
contracts = client.list_entity_contracts("ABCDEF123456", limit=25)
|
|
332
|
+
idvs = client.list_entity_idvs("ABCDEF123456")
|
|
333
|
+
otas = client.list_entity_otas("ABCDEF123456")
|
|
334
|
+
metrics = client.get_entity_metrics("ABCDEF123456", months=12, period_grouping="month")
|
|
296
335
|
```
|
|
297
336
|
|
|
298
337
|
## Pagination
|
|
@@ -310,9 +349,9 @@ print(f"Previous page URL: {response.previous}")
|
|
|
310
349
|
for contract in response.results:
|
|
311
350
|
print(contract['description'])
|
|
312
351
|
|
|
313
|
-
# Get next page
|
|
352
|
+
# Get next page (contracts use keyset/cursor pagination)
|
|
314
353
|
if response.next:
|
|
315
|
-
next_response = client.list_contracts(
|
|
354
|
+
next_response = client.list_contracts(cursor=response.cursor, limit=25)
|
|
316
355
|
```
|
|
317
356
|
|
|
318
357
|
## Error Handling
|
|
@@ -339,7 +378,7 @@ except TangoNotFoundError:
|
|
|
339
378
|
print("Resource not found")
|
|
340
379
|
except TangoValidationError as e:
|
|
341
380
|
print(f"Invalid parameters: {e.message}")
|
|
342
|
-
print(f"Details: {e.
|
|
381
|
+
print(f"Details: {e.response_data}")
|
|
343
382
|
except TangoRateLimitError:
|
|
344
383
|
print("Rate limit exceeded")
|
|
345
384
|
except TangoAPIError as e:
|
|
@@ -371,22 +410,18 @@ contracts = client.list_contracts(
|
|
|
371
410
|
|
|
372
411
|
### Flattened Responses
|
|
373
412
|
|
|
374
|
-
|
|
413
|
+
The `flat=True` parameter is passed to the API, which returns dot-notation keys in the raw response. The SDK still wraps the result in a `ShapedModel` — access nested fields via attribute or dict syntax, not dot-notation string keys:
|
|
375
414
|
|
|
376
415
|
```python
|
|
377
416
|
contracts = client.list_contracts(
|
|
378
417
|
shape="key,piid,recipient(display_name,uei)",
|
|
379
418
|
flat=True
|
|
380
419
|
)
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
flat=True,
|
|
387
|
-
flat_lists=True
|
|
388
|
-
)
|
|
389
|
-
# Returns: {"key": "...", "transactions.0.action_date": "...", "transactions.0.obligated": "..."}
|
|
420
|
+
for contract in contracts.results:
|
|
421
|
+
# Attribute access
|
|
422
|
+
print(contract.recipient.display_name)
|
|
423
|
+
# Dict access (nested, not flat string keys)
|
|
424
|
+
print(contract['recipient']['display_name'])
|
|
390
425
|
```
|
|
391
426
|
|
|
392
427
|
### Webhook Tooling
|
|
@@ -410,9 +445,8 @@ tango webhooks simulate --secret $SECRET --event-type entities.updated # sign +
|
|
|
410
445
|
tango webhooks simulate --secret $SECRET --event-type entities.updated \
|
|
411
446
|
--to http://127.0.0.1:8011/tango/webhooks # also POST
|
|
412
447
|
|
|
413
|
-
# Manage
|
|
414
|
-
tango webhooks endpoints
|
|
415
|
-
tango webhooks subscriptions create|list|get|delete
|
|
448
|
+
# Manage delivery endpoints
|
|
449
|
+
tango webhooks endpoints create|list|get|delete
|
|
416
450
|
|
|
417
451
|
# Force a real test delivery from Tango
|
|
418
452
|
tango webhooks trigger
|