tango-python 1.11.0__py3-none-any.whl → 1.13.0__py3-none-any.whl

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/__init__.py CHANGED
@@ -11,6 +11,9 @@ from .exceptions import (
11
11
  )
12
12
  from .models import (
13
13
  BudgetAccount,
14
+ BudgetAccountAnomalySource,
15
+ BudgetAccountAnomalySourceRow,
16
+ BudgetAccountSourceAnomaly,
14
17
  ContractAppeal,
15
18
  DibbsAward,
16
19
  DibbsRfp,
@@ -59,7 +62,7 @@ from .webhooks import (
59
62
  )
60
63
  from .webhooks.receiver import Delivery, WebhookReceiver
61
64
 
62
- __version__ = "1.11.0"
65
+ __version__ = "1.13.0"
63
66
  __all__ = [
64
67
  "TangoClient",
65
68
  "TangoAPIError",
@@ -72,6 +75,9 @@ __all__ = [
72
75
  "ResolveCandidate",
73
76
  "ResolveResult",
74
77
  "BudgetAccount",
78
+ "BudgetAccountAnomalySource",
79
+ "BudgetAccountAnomalySourceRow",
80
+ "BudgetAccountSourceAnomaly",
75
81
  "ContractAppeal",
76
82
  "FederalRegisterDocument",
77
83
  "EbuyAccess",
tango/client.py CHANGED
@@ -986,6 +986,7 @@ class TangoClient:
986
986
  next=data.get("next"),
987
987
  previous=data.get("previous"),
988
988
  results=results,
989
+ cursor=data.get("cursor"),
989
990
  page_metadata=data.get("page_metadata"),
990
991
  meta=data.get("meta"),
991
992
  )
@@ -1088,6 +1089,7 @@ class TangoClient:
1088
1089
  next=data.get("next"),
1089
1090
  previous=data.get("previous"),
1090
1091
  results=results,
1092
+ cursor=data.get("cursor"),
1091
1093
  page_metadata=data.get("page_metadata"),
1092
1094
  meta=data.get("meta"),
1093
1095
  )
@@ -1133,6 +1135,7 @@ class TangoClient:
1133
1135
  next=data.get("next"),
1134
1136
  previous=data.get("previous"),
1135
1137
  results=results,
1138
+ cursor=data.get("cursor"),
1136
1139
  page_metadata=data.get("page_metadata"),
1137
1140
  meta=data.get("meta"),
1138
1141
  )
@@ -1150,6 +1153,7 @@ class TangoClient:
1150
1153
  next=data.get("next"),
1151
1154
  previous=data.get("previous"),
1152
1155
  results=data.get("results") or [],
1156
+ cursor=data.get("cursor"),
1153
1157
  page_metadata=data.get("page_metadata"),
1154
1158
  meta=data.get("meta"),
1155
1159
  )
@@ -4444,6 +4448,12 @@ class TangoClient:
4444
4448
  bea_category: str | None = None,
4445
4449
  on_off_budget: str | None = None,
4446
4450
  subfunction_code: str | None = None,
4451
+ account_category: str | None = None,
4452
+ account_category_in: str | None = None,
4453
+ data_through_period: int | None = None,
4454
+ data_through_period_gte: int | None = None,
4455
+ data_through_period_lte: int | None = None,
4456
+ data_through_period_isnull: bool | None = None,
4447
4457
  # Range-numeric filters: each field also accepts ``__gte`` / ``__lte``.
4448
4458
  requested_ba: float | None = None,
4449
4459
  requested_ba_gte: float | None = None,
@@ -4550,6 +4560,13 @@ class TangoClient:
4550
4560
  bea_category: BEA category (exact).
4551
4561
  on_off_budget: On/off budget flag (exact).
4552
4562
  subfunction_code: Subfunction code (exact).
4563
+ account_category: Account category (exact), e.g. ``budgetary`` or
4564
+ ``credit_financing``. Treat the value as an open string; new
4565
+ categories may be added.
4566
+ account_category_in: Comma-separated account categories to match
4567
+ any of, e.g. ``"budgetary,credit_financing"``.
4568
+ data_through_period: File A period (1-12) the account-year's figures run through (exact). Also ``data_through_period_gte`` / ``data_through_period_lte``; a value below 12 means a partial year.
4569
+ data_through_period_isnull: ``True`` for accounts with no File A data, ``False`` for accounts with it.
4553
4570
  requested_ba: President's-budget requested BA (exact). Also
4554
4571
  ``requested_ba_gte`` / ``requested_ba_lte`` for range queries.
4555
4572
  enacted_ba: Enacted budget authority (exact / gte / lte).
@@ -4592,7 +4609,9 @@ class TangoClient:
4592
4609
  ordering: Sort field (prefix with '-' for descending). Any of the
4593
4610
  numeric fields above is a valid ordering target — e.g.
4594
4611
  ``ordering="-unobligated_balance"`` to rank by largest
4595
- headroom first.
4612
+ headroom first. The default ordering is the latest fiscal year
4613
+ first, then largest ``enacted_ba`` first, with accounts that
4614
+ have no ``enacted_ba`` sorted last.
4596
4615
  """
4597
4616
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
4598
4617
  if shape is None:
@@ -4615,11 +4634,19 @@ class TangoClient:
4615
4634
  ("bea_category", bea_category),
4616
4635
  ("on_off_budget", on_off_budget),
4617
4636
  ("subfunction_code", subfunction_code),
4637
+ ("account_category", account_category),
4638
+ ("account_category__in", account_category_in),
4639
+ ("data_through_period", data_through_period),
4640
+ ("data_through_period__gte", data_through_period_gte),
4641
+ ("data_through_period__lte", data_through_period_lte),
4642
+ ("data_through_period__isnull", data_through_period_isnull),
4618
4643
  ("search", search),
4619
4644
  ("ordering", ordering),
4620
4645
  )
4621
4646
  for key, val in scalar_filters:
4622
- if val is not None:
4647
+ if isinstance(val, bool):
4648
+ params[key] = "true" if val else "false"
4649
+ elif val is not None:
4623
4650
  params[key] = val
4624
4651
  # Range-numeric filters: each field has exact / __gte / __lte forms.
4625
4652
  range_filters: tuple[tuple[str, float | None, float | None, float | None], ...] = (
tango/models.py CHANGED
@@ -489,6 +489,8 @@ class Vehicle:
489
489
  program_acronym: str | None = None
490
490
  description: str | None = None
491
491
  idv_count: int | None = None
492
+ holder_count: int | None = None
493
+ order_winner_count: int | None = None
492
494
  total_obligated: Decimal | None = None
493
495
  latest_award_date: date | None = None
494
496
  solicitation_title: str | None = None
@@ -786,6 +788,55 @@ class ValidateResult:
786
788
  errors: list[str] | None = None
787
789
 
788
790
 
791
+ class BudgetAccountAnomalySourceRow(TypedDict, total=False):
792
+ """One source record behind a budget-account source anomaly.
793
+
794
+ Every key is optional and any value may be ``None``.
795
+ """
796
+
797
+ fiscal_period: int | None
798
+ piid: str | None
799
+ parent_piid: str | None
800
+ tas: str | None
801
+ reporting_agency_id: str | None
802
+ transaction_obligated_amount: float | str | None
803
+ file_c_source: str | None
804
+
805
+
806
+ class BudgetAccountAnomalySource(TypedDict, total=False):
807
+ """Where a budget-account source anomaly was found.
808
+
809
+ Every key is optional and any value may be ``None``.
810
+ """
811
+
812
+ dataset: str | None
813
+ fiscal_year: int | None
814
+ rows: list[BudgetAccountAnomalySourceRow] | None
815
+
816
+
817
+ class BudgetAccountSourceAnomaly(TypedDict, total=False):
818
+ """One problem found in the source data behind a budget account.
819
+
820
+ An element of ``BudgetAccount.source_anomalies``. Every key is optional and
821
+ any value may be ``None``, so read keys with ``.get()``. ``code`` and
822
+ ``action`` are open strings: today ``code`` is one of
823
+ ``contract_exceeds_obligations``, ``assistance_exceeds_obligations`` or
824
+ ``contract_without_obligations``, and ``action`` is ``capped`` or
825
+ ``flagged``, but new values may appear.
826
+ """
827
+
828
+ code: str | None
829
+ field: str | None
830
+ bound_field: str | None
831
+ action: str | None
832
+ reported_value: float | str | None
833
+ served_value: float | str | None
834
+ likely_cause: str | None
835
+ affected_fields: list[str] | None
836
+ message: str | None
837
+ source: BudgetAccountAnomalySource | None
838
+
839
+
789
840
  @dataclass
790
841
  class BudgetAccount:
791
842
  """Schema definition for BudgetAccount (not used for instances).
@@ -798,6 +849,8 @@ class BudgetAccount:
798
849
  id: int | None = None
799
850
  federal_account_symbol: str | None = None
800
851
  fiscal_year: int | None = None
852
+ # File A period (1-12) this account-year's figures run through; below 12 the year is partial, and `None` means no File A data.
853
+ data_through_period: int | None = None
801
854
  agency_code: str | None = None
802
855
  agency_name: str | None = None
803
856
  bureau_name: str | None = None
@@ -805,6 +858,8 @@ class BudgetAccount:
805
858
  bea_category: str | None = None
806
859
  on_off_budget: str | None = None
807
860
  subfunction_code: str | None = None
861
+ # ``budgetary`` or ``credit_financing`` today; treat as an open string.
862
+ account_category: str | None = None
808
863
  # Lifecycle
809
864
  requested_ba: Decimal | None = None
810
865
  enacted_ba: Decimal | None = None
@@ -828,6 +883,8 @@ class BudgetAccount:
828
883
  assistance_share_of_obligated: Decimal | None = None
829
884
  assistance_share_of_obligated_capped: Decimal | None = None
830
885
  assistance_share_capped_flag: bool | None = None
886
+ # Problems found in the source data; ``[]`` when there are none.
887
+ source_anomalies: list[BudgetAccountSourceAnomaly] | None = None
831
888
  # Forward-look
832
889
  next_year_requested_ba: Decimal | None = None
833
890
  ba_growth_next_year: Decimal | None = None
@@ -1699,7 +1756,7 @@ class ShapeConfig:
1699
1756
  VEHICLES_MINIMAL: Final = (
1700
1757
  "uuid,solicitation_identifier,is_synthetic_solicitation,program_acronym,"
1701
1758
  "organization_id,organization,vehicle_type,description,"
1702
- "idv_count,awardee_count,order_count,total_obligated,"
1759
+ "idv_count,holder_count,order_winner_count,awardee_count,order_count,total_obligated,"
1703
1760
  "vehicle_obligations,vehicle_contracts_value,latest_award_date,"
1704
1761
  "solicitation_title,solicitation_date"
1705
1762
  )
@@ -1711,7 +1768,8 @@ class ShapeConfig:
1711
1768
  "solicitation_title,solicitation_description,solicitation_date,opportunity_id,"
1712
1769
  "naics_code,psc_code,set_aside,"
1713
1770
  "fiscal_year,award_date,latest_award_date,last_date_to_order,"
1714
- "description,idv_count,awardee_count,order_count,total_obligated,"
1771
+ "description,idv_count,holder_count,order_winner_count,awardee_count,order_count,"
1772
+ "total_obligated,"
1715
1773
  "vehicle_obligations,vehicle_contracts_value,"
1716
1774
  "type_of_idc,contract_type,metrics(*)"
1717
1775
  )
@@ -1727,13 +1785,14 @@ class ShapeConfig:
1727
1785
  # Default for list_budget_accounts() / get_budget_account()
1728
1786
  # Mirrors the API's BUDGET_ACCOUNT_DEFAULT_SHAPE.
1729
1787
  BUDGET_ACCOUNTS_MINIMAL: Final = (
1730
- "id,federal_account_symbol,fiscal_year,agency_code,agency_name,bureau_name,"
1731
- "account_title,bea_category,on_off_budget,subfunction_code,"
1788
+ "id,federal_account_symbol,fiscal_year,data_through_period,agency_code,agency_name,"
1789
+ "bureau_name,"
1790
+ "account_title,bea_category,on_off_budget,subfunction_code,account_category,"
1732
1791
  "requested_ba,enacted_ba,apportioned,obligated_total,outlayed_total,"
1733
1792
  "unobligated_balance,contract_obligated,contract_share_of_obligated_capped,"
1734
1793
  "assistance_obligated,obligated_to_apportioned_pct_capped,"
1735
1794
  "obligated_to_enacted_pct_capped,outlayed_to_obligated_pct_capped,"
1736
- "ba_growth_next_year_pct"
1795
+ "ba_growth_next_year_pct,source_anomalies"
1737
1796
  )
1738
1797
 
1739
1798
  # Default for list_organizations()
@@ -1337,6 +1337,11 @@ VEHICLE_SCHEMA: dict[str, FieldSchema] = {
1337
1337
  ),
1338
1338
  # Denormalized rollups
1339
1339
  "idv_count": FieldSchema(name="idv_count", type=int, is_optional=True, is_list=False),
1340
+ "holder_count": FieldSchema(name="holder_count", type=int, is_optional=True, is_list=False),
1341
+ "order_winner_count": FieldSchema(
1342
+ name="order_winner_count", type=int, is_optional=True, is_list=False
1343
+ ),
1344
+ # Deprecated: same value as `order_winner_count`; removed at the next major API version.
1340
1345
  "awardee_count": FieldSchema(name="awardee_count", type=int, is_optional=True, is_list=False),
1341
1346
  "order_count": FieldSchema(name="order_count", type=int, is_optional=True, is_list=False),
1342
1347
  "total_obligated": FieldSchema(
@@ -1225,6 +1225,9 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
1225
1225
  ),
1226
1226
  },
1227
1227
  "BudgetAccount": {
1228
+ "account_category": FieldSchema(
1229
+ name="account_category", type=str, is_optional=True, is_list=False
1230
+ ),
1228
1231
  "account_narrative_excerpt": FieldSchema(
1229
1232
  name="account_narrative_excerpt", type=str, is_optional=True, is_list=False
1230
1233
  ),
@@ -1315,6 +1318,9 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
1315
1318
  name="contract_share_of_obligated_capped", type=str, is_optional=True, is_list=False
1316
1319
  ),
1317
1320
  "created": FieldSchema(name="created", type=datetime, is_optional=True, is_list=False),
1321
+ "data_through_period": FieldSchema(
1322
+ name="data_through_period", type=int, is_optional=True, is_list=False
1323
+ ),
1318
1324
  "enacted_ba": FieldSchema(name="enacted_ba", type=str, is_optional=True, is_list=False),
1319
1325
  "enacted_ba_5yr_cagr": FieldSchema(
1320
1326
  name="enacted_ba_5yr_cagr", type=str, is_optional=True, is_list=False
@@ -1410,6 +1416,9 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
1410
1416
  "requested_personnel_share": FieldSchema(
1411
1417
  name="requested_personnel_share", type=str, is_optional=True, is_list=False
1412
1418
  ),
1419
+ "source_anomalies": FieldSchema(
1420
+ name="source_anomalies", type=dict, is_optional=True, is_list=True
1421
+ ),
1413
1422
  "subfunction_code": FieldSchema(
1414
1423
  name="subfunction_code", type=str, is_optional=True, is_list=False
1415
1424
  ),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tango-python
3
- Version: 1.11.0
3
+ Version: 1.13.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
@@ -1,11 +1,11 @@
1
- tango/__init__.py,sha256=nM2IFpjrIua3MzTAR1bedo7DRmTgJ0DU9YeRQOlDXQA,2535
2
- tango/client.py,sha256=3jxYp3Dcgy8cVUpy0uK0H_qQbfKrgWMq3k65bm0uadg,238519
1
+ tango/__init__.py,sha256=L5_oz-laG934CX8HE8EOPEPW1a7JHmXo7dsTLwCGC5c,2739
2
+ tango/client.py,sha256=N-EKUT2OquRrjpInxK0ZnWVTuDP5ylEXadshjvxO6ng,240281
3
3
  tango/exceptions.py,sha256=oPiLCccBoWLK5SjHkzP-oI6mUD9twEeKSvEuLV5h01I,4224
4
- tango/models.py,sha256=WkIK_Ym36afRych_EtlGk5Ki3aevL14klk0SCiCDxgU,67741
4
+ tango/models.py,sha256=w7z49WIMcvvEt05J1DpuUPcFWBq_MbCWa4a-f-YLsVs,69936
5
5
  tango/shapes/__init__.py,sha256=7ea1WU74jp4znhNw-gXruag6m6eyPZtbVgbDFmFUWro,1072
6
- tango/shapes/explicit_schemas.py,sha256=spYyMmWKOiMJygz9yViBQrjIxUPFfv7HpVkJcWy13x8,84021
6
+ tango/shapes/explicit_schemas.py,sha256=tqGitlPoXN4VDfwCU5T4KfzLfQTyZ2Ephap56bDbB3c,84334
7
7
  tango/shapes/factory.py,sha256=uDPhnp6VVzrLBKodxteZy5AcU43juEv-qKVRWth51j0,31550
8
- tango/shapes/generated_overlay.py,sha256=RrCqR-iwBnUbr5dtPQ_f78fnrWcfLf_fP9EtfHKIdw4,140461
8
+ tango/shapes/generated_overlay.py,sha256=fyCgKDueIKUcTMtij793mtJcAsC8RynMhU5FXI_9hG0,140860
9
9
  tango/shapes/generator.py,sha256=xzakpwTwqTMUr89I06hp54-FIYZziviWHk2Glscg-LM,22775
10
10
  tango/shapes/models.py,sha256=h3pIhOqrrdlN953Y6r0oney5HFbKPOD-frRndRWimJ0,3018
11
11
  tango/shapes/parser.py,sha256=AecNx5oT_hV_pf2AWZnsR8Y2aLmCtjfgS9-znA2Pesc,27429
@@ -16,8 +16,8 @@ tango/webhooks/cli.py,sha256=f_vQbJ2AeSQjKnQo7MxHFyUB2SAKcgHYmGQULS0isqQ,13858
16
16
  tango/webhooks/receiver.py,sha256=5yhsVhlLyoxmOCGvmbynWAIlDB2OaCPVf1H4GA1SxmU,9279
17
17
  tango/webhooks/signing.py,sha256=92Ee-0B6PR7ZkvY3Np3gzl88-mtfKkh-I7lxqCe2lGw,2374
18
18
  tango/webhooks/simulate.py,sha256=g2Osa0FYU5mJuon07T2aUCtmkUoTEzsY261tlp76fF0,3165
19
- tango_python-1.11.0.dist-info/METADATA,sha256=l9-XAukNLKq3XM_V8zjXOzALwqo6MeGBIFtdEsW9kuY,22118
20
- tango_python-1.11.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
21
- tango_python-1.11.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
22
- tango_python-1.11.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
23
- tango_python-1.11.0.dist-info/RECORD,,
19
+ tango_python-1.13.0.dist-info/METADATA,sha256=fzPg4uS9gC6BNoxJY3lB0b9-FXrX-6mlkkCq0xIX9Wc,22118
20
+ tango_python-1.13.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
21
+ tango_python-1.13.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
22
+ tango_python-1.13.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
23
+ tango_python-1.13.0.dist-info/RECORD,,