tango-python 0.6.0__tar.gz → 1.0.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.0.0/.github/workflows/docs-dispatch.yml +58 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/.gitignore +25 -1
- {tango_python-0.6.0 → tango_python-1.0.0}/CHANGELOG.md +61 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/PKG-INFO +51 -21
- {tango_python-0.6.0 → tango_python-1.0.0}/README.md +50 -20
- {tango_python-0.6.0 → tango_python-1.0.0}/docs/API_REFERENCE.md +366 -69
- tango_python-1.0.0/docs/CLIENT.md +73 -0
- tango_python-1.0.0/docs/DYNAMIC_MODELS.md +205 -0
- tango_python-1.0.0/docs/ERRORS.md +158 -0
- tango_python-1.0.0/docs/PAGINATION.md +112 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/docs/SHAPES.md +32 -28
- {tango_python-0.6.0 → tango_python-1.0.0}/docs/WEBHOOKS.md +73 -54
- {tango_python-0.6.0 → tango_python-1.0.0}/pyproject.toml +29 -1
- tango_python-1.0.0/scripts/smoke_api_parity.py +397 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/__init__.py +17 -5
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/client.py +923 -140
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/models.py +115 -26
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/shapes/explicit_schemas.py +173 -2
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/shapes/parser.py +60 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/webhooks/cli.py +16 -108
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/webhooks/receiver.py +9 -2
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/webhooks/signing.py +19 -6
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/webhooks/simulate.py +7 -6
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/production/test_production_smoke.py +4 -8
- tango_python-1.0.0/tests/test_api_parity.py +474 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/test_client.py +4 -105
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/test_shapes.py +136 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/test_webhooks_cli.py +10 -79
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/test_webhooks_receiver.py +3 -2
- {tango_python-0.6.0 → tango_python-1.0.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/.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/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/uv.lock +0 -2225
- {tango_python-0.6.0 → tango_python-1.0.0}/.github/workflows/lint.yml +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/.github/workflows/publish.yml +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/.github/workflows/test.yml +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/LICENSE +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/scripts/README.md +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/scripts/check_filter_shape_conformance.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/scripts/fetch_api_schema.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/scripts/generate_schemas_from_api.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/scripts/pr_review.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/scripts/test_production.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/exceptions.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/shapes/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/shapes/factory.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/shapes/generator.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/shapes/models.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/shapes/schema.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/shapes/types.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tango/webhooks/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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-0.6.0 → tango_python-1.0.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-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.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.0.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.0.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.0.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.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.0.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.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.0.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.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.0.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.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.0.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.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.0.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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.0.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.0.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.0.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.0.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.0.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.0.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/conftest.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/README.md +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/conftest.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_agencies_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_contracts_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_edge_cases_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_entities_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_forecasts_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_grants_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_itdashboard_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_naics_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_notices_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_offices_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_opportunities_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_organizations_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_protests_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_reference_data_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_subawards_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/test_vehicles_idvs_integration.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/integration/validation.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/production/__init__.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/production/conftest.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.0}/tests/test_models.py +0 -0
- {tango_python-0.6.0 → tango_python-1.0.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 }}"
|
|
@@ -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
|
|
@@ -5,6 +5,67 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.0.0] - 2026-05-13
|
|
11
|
+
|
|
12
|
+
> First stable release. `tango-python` is now at full API parity with the
|
|
13
|
+
> Tango HTTP surface, the legacy subject-based webhook subscription
|
|
14
|
+
> mechanism has been removed in favor of filter alerts, the shape parser
|
|
15
|
+
> agrees byte-for-byte with the server's expand-alias handling, and the
|
|
16
|
+
> SDK's docs are auto-published to `docs.makegov.com/sdks/python/` via the
|
|
17
|
+
> composer pipeline (makegov/docs#15 / makegov/docs#16). From `1.x` on,
|
|
18
|
+
> we'll only do breaking changes on a major bump.
|
|
19
|
+
>
|
|
20
|
+
> Originally tracked as: API parity (PR #25), subject-based webhook
|
|
21
|
+
> removal (PR #27 / issue #2275), shape-validator alias support (PR #28 /
|
|
22
|
+
> issue #2266), and the docs-only content port (makegov/docs#16).
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- `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).
|
|
26
|
+
- `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.)
|
|
27
|
+
- `update_webhook_endpoint` accepts `name=` for renaming an endpoint.
|
|
28
|
+
- 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.
|
|
29
|
+
- `resolve(name, target_type, ...)` — POST `/api/resolve/` to rank entity / organization candidates from a free-text name. Returns `ResolveResult` with `ResolveCandidate` entries (both exported).
|
|
30
|
+
- `validate(identifier_type, value)` — POST `/api/validate/` to validate the format of a PIID, solicitation number, or UEI. Returns `ValidateResult` (exported).
|
|
31
|
+
- 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`.
|
|
32
|
+
- 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.
|
|
33
|
+
- IDV sub-resources: `list_idv_lcats`.
|
|
34
|
+
- Agency sub-resources: `list_agency_awarding_contracts`, `list_agency_funding_contracts`.
|
|
35
|
+
- 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/`.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- `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).
|
|
39
|
+
- `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.
|
|
40
|
+
- **`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.
|
|
41
|
+
|
|
42
|
+
### Removed
|
|
43
|
+
- **Subject-based webhook subscription surface** (tango#2275). Migrate to `create_webhook_alert(...)` and the alerts API.
|
|
44
|
+
- Methods: `list_webhook_subscriptions`, `get_webhook_subscription`, `create_webhook_subscription`, `update_webhook_subscription`, `delete_webhook_subscription`.
|
|
45
|
+
- Dataclasses: `WebhookSubscription`, `WebhookSubjectTypeDefinition`. Both are no longer exported from the top-level `tango` package — importing them raises `ImportError`.
|
|
46
|
+
- 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.
|
|
47
|
+
- 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.
|
|
48
|
+
- `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`.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
- `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.
|
|
52
|
+
- `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"`.
|
|
53
|
+
- `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.
|
|
54
|
+
- `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`.
|
|
55
|
+
- `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.
|
|
56
|
+
- `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)`).
|
|
57
|
+
- `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.
|
|
58
|
+
- **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.
|
|
59
|
+
- **`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.
|
|
60
|
+
|
|
61
|
+
### Documentation
|
|
62
|
+
- 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).
|
|
63
|
+
- 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`.
|
|
64
|
+
- 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`.
|
|
65
|
+
|
|
66
|
+
### CI
|
|
67
|
+
- 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.
|
|
68
|
+
|
|
8
69
|
## [0.6.0] - 2026-05-07
|
|
9
70
|
|
|
10
71
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tango-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.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
|
|
@@ -96,12 +96,14 @@ print(f"Found {agencies.count} agencies")
|
|
|
96
96
|
|
|
97
97
|
# Get specific agency
|
|
98
98
|
agency = client.get_agency("GSA")
|
|
99
|
-
print(f"Agency: {agency
|
|
99
|
+
print(f"Agency: {agency.name}")
|
|
100
100
|
|
|
101
101
|
# Search contracts
|
|
102
102
|
contracts = client.list_contracts(
|
|
103
103
|
limit=10
|
|
104
104
|
)
|
|
105
|
+
```
|
|
106
|
+
|
|
105
107
|
## Authentication
|
|
106
108
|
|
|
107
109
|
Most endpoints require an API key. You can obtain one from the [Tango API portal](https://tango.makegov.com).
|
|
@@ -261,13 +263,13 @@ opportunities = client.list_opportunities(agency="DOD", active=True, limit=25)
|
|
|
261
263
|
### Notices
|
|
262
264
|
|
|
263
265
|
```python
|
|
264
|
-
notices = client.list_notices(agency="DOD", notice_type="
|
|
266
|
+
notices = client.list_notices(agency="DOD", notice_type="Presolicitation", limit=25)
|
|
265
267
|
```
|
|
266
268
|
|
|
267
269
|
### Grants
|
|
268
270
|
|
|
269
271
|
```python
|
|
270
|
-
grants = client.list_grants(agency="HHS", status="
|
|
272
|
+
grants = client.list_grants(agency="HHS", status="F", limit=25) # F = Forecasted
|
|
271
273
|
```
|
|
272
274
|
|
|
273
275
|
### Protests
|
|
@@ -287,12 +289,45 @@ contract = client.get_gsa_elibrary_contract("UUID")
|
|
|
287
289
|
### Reference Data
|
|
288
290
|
|
|
289
291
|
```python
|
|
290
|
-
# Offices, organizations, NAICS, subawards, business types
|
|
292
|
+
# Offices, organizations, NAICS, PSC, subawards, business types
|
|
291
293
|
offices = client.list_offices(search="acquisitions")
|
|
292
294
|
organizations = client.list_organizations(level=1)
|
|
293
295
|
naics = client.list_naics(search="software")
|
|
296
|
+
get_naics = client.get_naics("541511")
|
|
297
|
+
psc = client.list_psc()
|
|
294
298
|
subawards = client.list_subawards(prime_uei="UEI123")
|
|
295
299
|
business_types = client.list_business_types()
|
|
300
|
+
mas_sins = client.list_mas_sins()
|
|
301
|
+
assistance = client.list_assistance_listings()
|
|
302
|
+
departments = client.list_departments()
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Resolve / Validate
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
# Resolve a name to entity/org candidates
|
|
309
|
+
result = client.resolve(name="Lockheed Martin", target_type="entity")
|
|
310
|
+
for c in result.candidates:
|
|
311
|
+
print(c.identifier, c.display_name)
|
|
312
|
+
|
|
313
|
+
# Validate an identifier
|
|
314
|
+
result = client.validate(identifier_type="uei", value="ABCDEF123456")
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### IT Dashboard
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
investments = client.list_itdashboard_investments(search="cloud", limit=25)
|
|
321
|
+
investment = client.get_itdashboard_investment("023-000001234")
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Entity Sub-resources
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
contracts = client.list_entity_contracts("ABCDEF123456", limit=25)
|
|
328
|
+
idvs = client.list_entity_idvs("ABCDEF123456")
|
|
329
|
+
otas = client.list_entity_otas("ABCDEF123456")
|
|
330
|
+
metrics = client.get_entity_metrics("ABCDEF123456", months=12, period_grouping="month")
|
|
296
331
|
```
|
|
297
332
|
|
|
298
333
|
## Pagination
|
|
@@ -310,9 +345,9 @@ print(f"Previous page URL: {response.previous}")
|
|
|
310
345
|
for contract in response.results:
|
|
311
346
|
print(contract['description'])
|
|
312
347
|
|
|
313
|
-
# Get next page
|
|
348
|
+
# Get next page (contracts use keyset/cursor pagination)
|
|
314
349
|
if response.next:
|
|
315
|
-
next_response = client.list_contracts(
|
|
350
|
+
next_response = client.list_contracts(cursor=response.cursor, limit=25)
|
|
316
351
|
```
|
|
317
352
|
|
|
318
353
|
## Error Handling
|
|
@@ -339,7 +374,7 @@ except TangoNotFoundError:
|
|
|
339
374
|
print("Resource not found")
|
|
340
375
|
except TangoValidationError as e:
|
|
341
376
|
print(f"Invalid parameters: {e.message}")
|
|
342
|
-
print(f"Details: {e.
|
|
377
|
+
print(f"Details: {e.response_data}")
|
|
343
378
|
except TangoRateLimitError:
|
|
344
379
|
print("Rate limit exceeded")
|
|
345
380
|
except TangoAPIError as e:
|
|
@@ -371,22 +406,18 @@ contracts = client.list_contracts(
|
|
|
371
406
|
|
|
372
407
|
### Flattened Responses
|
|
373
408
|
|
|
374
|
-
|
|
409
|
+
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
410
|
|
|
376
411
|
```python
|
|
377
412
|
contracts = client.list_contracts(
|
|
378
413
|
shape="key,piid,recipient(display_name,uei)",
|
|
379
414
|
flat=True
|
|
380
415
|
)
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
flat=True,
|
|
387
|
-
flat_lists=True
|
|
388
|
-
)
|
|
389
|
-
# Returns: {"key": "...", "transactions.0.action_date": "...", "transactions.0.obligated": "..."}
|
|
416
|
+
for contract in contracts.results:
|
|
417
|
+
# Attribute access
|
|
418
|
+
print(contract.recipient.display_name)
|
|
419
|
+
# Dict access (nested, not flat string keys)
|
|
420
|
+
print(contract['recipient']['display_name'])
|
|
390
421
|
```
|
|
391
422
|
|
|
392
423
|
### Webhook Tooling
|
|
@@ -410,9 +441,8 @@ tango webhooks simulate --secret $SECRET --event-type entities.updated # sign +
|
|
|
410
441
|
tango webhooks simulate --secret $SECRET --event-type entities.updated \
|
|
411
442
|
--to http://127.0.0.1:8011/tango/webhooks # also POST
|
|
412
443
|
|
|
413
|
-
# Manage
|
|
414
|
-
tango webhooks endpoints
|
|
415
|
-
tango webhooks subscriptions create|list|get|delete
|
|
444
|
+
# Manage delivery endpoints
|
|
445
|
+
tango webhooks endpoints create|list|get|delete
|
|
416
446
|
|
|
417
447
|
# Force a real test delivery from Tango
|
|
418
448
|
tango webhooks trigger
|
|
@@ -39,12 +39,14 @@ print(f"Found {agencies.count} agencies")
|
|
|
39
39
|
|
|
40
40
|
# Get specific agency
|
|
41
41
|
agency = client.get_agency("GSA")
|
|
42
|
-
print(f"Agency: {agency
|
|
42
|
+
print(f"Agency: {agency.name}")
|
|
43
43
|
|
|
44
44
|
# Search contracts
|
|
45
45
|
contracts = client.list_contracts(
|
|
46
46
|
limit=10
|
|
47
47
|
)
|
|
48
|
+
```
|
|
49
|
+
|
|
48
50
|
## Authentication
|
|
49
51
|
|
|
50
52
|
Most endpoints require an API key. You can obtain one from the [Tango API portal](https://tango.makegov.com).
|
|
@@ -204,13 +206,13 @@ opportunities = client.list_opportunities(agency="DOD", active=True, limit=25)
|
|
|
204
206
|
### Notices
|
|
205
207
|
|
|
206
208
|
```python
|
|
207
|
-
notices = client.list_notices(agency="DOD", notice_type="
|
|
209
|
+
notices = client.list_notices(agency="DOD", notice_type="Presolicitation", limit=25)
|
|
208
210
|
```
|
|
209
211
|
|
|
210
212
|
### Grants
|
|
211
213
|
|
|
212
214
|
```python
|
|
213
|
-
grants = client.list_grants(agency="HHS", status="
|
|
215
|
+
grants = client.list_grants(agency="HHS", status="F", limit=25) # F = Forecasted
|
|
214
216
|
```
|
|
215
217
|
|
|
216
218
|
### Protests
|
|
@@ -230,12 +232,45 @@ contract = client.get_gsa_elibrary_contract("UUID")
|
|
|
230
232
|
### Reference Data
|
|
231
233
|
|
|
232
234
|
```python
|
|
233
|
-
# Offices, organizations, NAICS, subawards, business types
|
|
235
|
+
# Offices, organizations, NAICS, PSC, subawards, business types
|
|
234
236
|
offices = client.list_offices(search="acquisitions")
|
|
235
237
|
organizations = client.list_organizations(level=1)
|
|
236
238
|
naics = client.list_naics(search="software")
|
|
239
|
+
get_naics = client.get_naics("541511")
|
|
240
|
+
psc = client.list_psc()
|
|
237
241
|
subawards = client.list_subawards(prime_uei="UEI123")
|
|
238
242
|
business_types = client.list_business_types()
|
|
243
|
+
mas_sins = client.list_mas_sins()
|
|
244
|
+
assistance = client.list_assistance_listings()
|
|
245
|
+
departments = client.list_departments()
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Resolve / Validate
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
# Resolve a name to entity/org candidates
|
|
252
|
+
result = client.resolve(name="Lockheed Martin", target_type="entity")
|
|
253
|
+
for c in result.candidates:
|
|
254
|
+
print(c.identifier, c.display_name)
|
|
255
|
+
|
|
256
|
+
# Validate an identifier
|
|
257
|
+
result = client.validate(identifier_type="uei", value="ABCDEF123456")
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### IT Dashboard
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
investments = client.list_itdashboard_investments(search="cloud", limit=25)
|
|
264
|
+
investment = client.get_itdashboard_investment("023-000001234")
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Entity Sub-resources
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
contracts = client.list_entity_contracts("ABCDEF123456", limit=25)
|
|
271
|
+
idvs = client.list_entity_idvs("ABCDEF123456")
|
|
272
|
+
otas = client.list_entity_otas("ABCDEF123456")
|
|
273
|
+
metrics = client.get_entity_metrics("ABCDEF123456", months=12, period_grouping="month")
|
|
239
274
|
```
|
|
240
275
|
|
|
241
276
|
## Pagination
|
|
@@ -253,9 +288,9 @@ print(f"Previous page URL: {response.previous}")
|
|
|
253
288
|
for contract in response.results:
|
|
254
289
|
print(contract['description'])
|
|
255
290
|
|
|
256
|
-
# Get next page
|
|
291
|
+
# Get next page (contracts use keyset/cursor pagination)
|
|
257
292
|
if response.next:
|
|
258
|
-
next_response = client.list_contracts(
|
|
293
|
+
next_response = client.list_contracts(cursor=response.cursor, limit=25)
|
|
259
294
|
```
|
|
260
295
|
|
|
261
296
|
## Error Handling
|
|
@@ -282,7 +317,7 @@ except TangoNotFoundError:
|
|
|
282
317
|
print("Resource not found")
|
|
283
318
|
except TangoValidationError as e:
|
|
284
319
|
print(f"Invalid parameters: {e.message}")
|
|
285
|
-
print(f"Details: {e.
|
|
320
|
+
print(f"Details: {e.response_data}")
|
|
286
321
|
except TangoRateLimitError:
|
|
287
322
|
print("Rate limit exceeded")
|
|
288
323
|
except TangoAPIError as e:
|
|
@@ -314,22 +349,18 @@ contracts = client.list_contracts(
|
|
|
314
349
|
|
|
315
350
|
### Flattened Responses
|
|
316
351
|
|
|
317
|
-
|
|
352
|
+
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:
|
|
318
353
|
|
|
319
354
|
```python
|
|
320
355
|
contracts = client.list_contracts(
|
|
321
356
|
shape="key,piid,recipient(display_name,uei)",
|
|
322
357
|
flat=True
|
|
323
358
|
)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
flat=True,
|
|
330
|
-
flat_lists=True
|
|
331
|
-
)
|
|
332
|
-
# Returns: {"key": "...", "transactions.0.action_date": "...", "transactions.0.obligated": "..."}
|
|
359
|
+
for contract in contracts.results:
|
|
360
|
+
# Attribute access
|
|
361
|
+
print(contract.recipient.display_name)
|
|
362
|
+
# Dict access (nested, not flat string keys)
|
|
363
|
+
print(contract['recipient']['display_name'])
|
|
333
364
|
```
|
|
334
365
|
|
|
335
366
|
### Webhook Tooling
|
|
@@ -353,9 +384,8 @@ tango webhooks simulate --secret $SECRET --event-type entities.updated # sign +
|
|
|
353
384
|
tango webhooks simulate --secret $SECRET --event-type entities.updated \
|
|
354
385
|
--to http://127.0.0.1:8011/tango/webhooks # also POST
|
|
355
386
|
|
|
356
|
-
# Manage
|
|
357
|
-
tango webhooks endpoints
|
|
358
|
-
tango webhooks subscriptions create|list|get|delete
|
|
387
|
+
# Manage delivery endpoints
|
|
388
|
+
tango webhooks endpoints create|list|get|delete
|
|
359
389
|
|
|
360
390
|
# Force a real test delivery from Tango
|
|
361
391
|
tango webhooks trigger
|