tango-python 1.13.0__py3-none-any.whl → 1.15.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
@@ -62,7 +62,7 @@ from .webhooks import (
62
62
  )
63
63
  from .webhooks.receiver import Delivery, WebhookReceiver
64
64
 
65
- __version__ = "1.13.0"
65
+ __version__ = "1.15.0"
66
66
  __all__ = [
67
67
  "TangoClient",
68
68
  "TangoAPIError",
tango/client.py CHANGED
@@ -71,6 +71,9 @@ from tango.shapes import (
71
71
  build_parser_registry_from_client,
72
72
  )
73
73
 
74
+ # Where `_request` stores a list response's `X-Results-CountType` header so each `PaginatedResponse` reads its own, not the shared `last_response_headers`.
75
+ _COUNT_TYPE_KEY = "_tango_count_type"
76
+
74
77
 
75
78
  class TangoClient:
76
79
  """Tango API Client"""
@@ -180,7 +183,11 @@ class TangoClient:
180
183
 
181
184
  self._raise_for_status(response)
182
185
 
183
- return response.json() if response.content else {}
186
+ data = response.json() if response.content else {}
187
+ count_type = response.headers.get("X-Results-CountType")
188
+ if isinstance(count_type, str) and isinstance(data, dict) and "count" in data:
189
+ data[_COUNT_TYPE_KEY] = count_type.lower()
190
+ return data
184
191
 
185
192
  except httpx.HTTPError as e:
186
193
  raise TangoAPIError(f"Request failed: {str(e)}") from e
@@ -495,6 +502,7 @@ class TangoClient:
495
502
  return PaginatedResponse(
496
503
  count=data["count"],
497
504
  next=data.get("next"),
505
+ count_type=data.get(_COUNT_TYPE_KEY),
498
506
  previous=data.get("previous"),
499
507
  results=[
500
508
  ag
@@ -530,6 +538,7 @@ class TangoClient:
530
538
  return PaginatedResponse(
531
539
  count=data.get("count", 0),
532
540
  next=data.get("next"),
541
+ count_type=data.get(_COUNT_TYPE_KEY),
533
542
  previous=data.get("previous"),
534
543
  results=data.get("results", []),
535
544
  meta=data.get("meta"),
@@ -583,6 +592,7 @@ class TangoClient:
583
592
  return PaginatedResponse(
584
593
  count=data.get("count", 0),
585
594
  next=data.get("next"),
595
+ count_type=data.get(_COUNT_TYPE_KEY),
586
596
  previous=data.get("previous"),
587
597
  results=results,
588
598
  meta=data.get("meta"),
@@ -795,6 +805,7 @@ class TangoClient:
795
805
  return PaginatedResponse(
796
806
  count=data["count"],
797
807
  next=data.get("next"),
808
+ count_type=data.get(_COUNT_TYPE_KEY),
798
809
  previous=data.get("previous"),
799
810
  results=results,
800
811
  cursor=data.get("cursor"),
@@ -857,6 +868,7 @@ class TangoClient:
857
868
  return PaginatedResponse(
858
869
  count=int(data.get("count") or len(results)),
859
870
  next=data.get("next"),
871
+ count_type=data.get(_COUNT_TYPE_KEY),
860
872
  previous=data.get("previous"),
861
873
  results=results,
862
874
  cursor=data.get("cursor"),
@@ -880,6 +892,7 @@ class TangoClient:
880
892
  return PaginatedResponse(
881
893
  count=int(data.get("count") or len(data.get("results") or [])),
882
894
  next=data.get("next"),
895
+ count_type=data.get(_COUNT_TYPE_KEY),
883
896
  previous=data.get("previous"),
884
897
  results=data.get("results") or [],
885
898
  cursor=data.get("cursor"),
@@ -984,6 +997,7 @@ class TangoClient:
984
997
  return PaginatedResponse(
985
998
  count=int(data.get("count") or len(results)),
986
999
  next=data.get("next"),
1000
+ count_type=data.get(_COUNT_TYPE_KEY),
987
1001
  previous=data.get("previous"),
988
1002
  results=results,
989
1003
  cursor=data.get("cursor"),
@@ -1087,6 +1101,7 @@ class TangoClient:
1087
1101
  return PaginatedResponse(
1088
1102
  count=int(data.get("count") or len(results)),
1089
1103
  next=data.get("next"),
1104
+ count_type=data.get(_COUNT_TYPE_KEY),
1090
1105
  previous=data.get("previous"),
1091
1106
  results=results,
1092
1107
  cursor=data.get("cursor"),
@@ -1133,6 +1148,7 @@ class TangoClient:
1133
1148
  return PaginatedResponse(
1134
1149
  count=int(data.get("count") or len(results)),
1135
1150
  next=data.get("next"),
1151
+ count_type=data.get(_COUNT_TYPE_KEY),
1136
1152
  previous=data.get("previous"),
1137
1153
  results=results,
1138
1154
  cursor=data.get("cursor"),
@@ -1151,6 +1167,7 @@ class TangoClient:
1151
1167
  return PaginatedResponse(
1152
1168
  count=int(data.get("count") or len(data.get("results") or [])),
1153
1169
  next=data.get("next"),
1170
+ count_type=data.get(_COUNT_TYPE_KEY),
1154
1171
  previous=data.get("previous"),
1155
1172
  results=data.get("results") or [],
1156
1173
  cursor=data.get("cursor"),
@@ -1234,6 +1251,7 @@ class TangoClient:
1234
1251
  return PaginatedResponse(
1235
1252
  count=int(data.get("count") or len(results)),
1236
1253
  next=data.get("next"),
1254
+ count_type=data.get(_COUNT_TYPE_KEY),
1237
1255
  previous=data.get("previous"),
1238
1256
  results=results,
1239
1257
  cursor=data.get("cursor"),
@@ -1340,6 +1358,7 @@ class TangoClient:
1340
1358
  return PaginatedResponse(
1341
1359
  count=int(data.get("count") or len(results)),
1342
1360
  next=data.get("next"),
1361
+ count_type=data.get(_COUNT_TYPE_KEY),
1343
1362
  previous=data.get("previous"),
1344
1363
  results=results,
1345
1364
  cursor=data.get("cursor"),
@@ -1417,6 +1436,7 @@ class TangoClient:
1417
1436
  return PaginatedResponse(
1418
1437
  count=int(data.get("count") or len(results)),
1419
1438
  next=data.get("next"),
1439
+ count_type=data.get(_COUNT_TYPE_KEY),
1420
1440
  previous=data.get("previous"),
1421
1441
  results=results,
1422
1442
  cursor=data.get("cursor"),
@@ -1474,6 +1494,7 @@ class TangoClient:
1474
1494
  return PaginatedResponse(
1475
1495
  count=data.get("count", 0),
1476
1496
  next=data.get("next"),
1497
+ count_type=data.get(_COUNT_TYPE_KEY),
1477
1498
  previous=data.get("previous"),
1478
1499
  results=results,
1479
1500
  meta=data.get("meta"),
@@ -1559,6 +1580,7 @@ class TangoClient:
1559
1580
  return PaginatedResponse(
1560
1581
  count=data.get("count", 0),
1561
1582
  next=data.get("next"),
1583
+ count_type=data.get(_COUNT_TYPE_KEY),
1562
1584
  previous=data.get("previous"),
1563
1585
  results=results,
1564
1586
  meta=data.get("meta"),
@@ -1671,6 +1693,7 @@ class TangoClient:
1671
1693
  return PaginatedResponse(
1672
1694
  count=data.get("count", 0),
1673
1695
  next=data.get("next"),
1696
+ count_type=data.get(_COUNT_TYPE_KEY),
1674
1697
  previous=data.get("previous"),
1675
1698
  results=results,
1676
1699
  meta=data.get("meta"),
@@ -1841,6 +1864,7 @@ class TangoClient:
1841
1864
  return PaginatedResponse(
1842
1865
  count=data["count"],
1843
1866
  next=data.get("next"),
1867
+ count_type=data.get(_COUNT_TYPE_KEY),
1844
1868
  previous=data.get("previous"),
1845
1869
  results=results,
1846
1870
  meta=data.get("meta"),
@@ -1923,6 +1947,7 @@ class TangoClient:
1923
1947
  return PaginatedResponse(
1924
1948
  count=data["count"],
1925
1949
  next=data.get("next"),
1950
+ count_type=data.get(_COUNT_TYPE_KEY),
1926
1951
  previous=data.get("previous"),
1927
1952
  results=results,
1928
1953
  meta=data.get("meta"),
@@ -1972,6 +1997,7 @@ class TangoClient:
1972
1997
  return PaginatedResponse(
1973
1998
  count=data["count"],
1974
1999
  next=data.get("next"),
2000
+ count_type=data.get(_COUNT_TYPE_KEY),
1975
2001
  previous=data.get("previous"),
1976
2002
  results=results,
1977
2003
  meta=data.get("meta"),
@@ -2007,6 +2033,7 @@ class TangoClient:
2007
2033
  return PaginatedResponse(
2008
2034
  count=data["count"],
2009
2035
  next=data.get("next"),
2036
+ count_type=data.get(_COUNT_TYPE_KEY),
2010
2037
  previous=data.get("previous"),
2011
2038
  results=results,
2012
2039
  meta=data.get("meta"),
@@ -2059,6 +2086,7 @@ class TangoClient:
2059
2086
  return PaginatedResponse(
2060
2087
  count=data.get("count", 0),
2061
2088
  next=data.get("next"),
2089
+ count_type=data.get(_COUNT_TYPE_KEY),
2062
2090
  previous=data.get("previous"),
2063
2091
  results=data.get("results", []),
2064
2092
  meta=data.get("meta"),
@@ -2146,6 +2174,7 @@ class TangoClient:
2146
2174
  return PaginatedResponse(
2147
2175
  count=data["count"],
2148
2176
  next=data.get("next"),
2177
+ count_type=data.get(_COUNT_TYPE_KEY),
2149
2178
  previous=data.get("previous"),
2150
2179
  results=results,
2151
2180
  meta=data.get("meta"),
@@ -2203,6 +2232,7 @@ class TangoClient:
2203
2232
  return PaginatedResponse(
2204
2233
  count=int(data.get("count", 0)),
2205
2234
  next=data.get("next"),
2235
+ count_type=data.get(_COUNT_TYPE_KEY),
2206
2236
  previous=data.get("previous"),
2207
2237
  results=list(data.get("results") or []),
2208
2238
  meta=data.get("meta"),
@@ -2296,6 +2326,7 @@ class TangoClient:
2296
2326
  return PaginatedResponse(
2297
2327
  count=data["count"],
2298
2328
  next=data.get("next"),
2329
+ count_type=data.get(_COUNT_TYPE_KEY),
2299
2330
  previous=data.get("previous"),
2300
2331
  results=results,
2301
2332
  meta=data.get("meta"),
@@ -2427,6 +2458,7 @@ class TangoClient:
2427
2458
  return PaginatedResponse(
2428
2459
  count=data["count"],
2429
2460
  next=data.get("next"),
2461
+ count_type=data.get(_COUNT_TYPE_KEY),
2430
2462
  previous=data.get("previous"),
2431
2463
  results=results,
2432
2464
  meta=data.get("meta"),
@@ -2542,6 +2574,7 @@ class TangoClient:
2542
2574
  return PaginatedResponse(
2543
2575
  count=data["count"],
2544
2576
  next=data.get("next"),
2577
+ count_type=data.get(_COUNT_TYPE_KEY),
2545
2578
  previous=data.get("previous"),
2546
2579
  results=results,
2547
2580
  meta=data.get("meta"),
@@ -2664,6 +2697,7 @@ class TangoClient:
2664
2697
  return PaginatedResponse(
2665
2698
  count=data["count"],
2666
2699
  next=data.get("next"),
2700
+ count_type=data.get(_COUNT_TYPE_KEY),
2667
2701
  previous=data.get("previous"),
2668
2702
  results=results,
2669
2703
  meta=data.get("meta"),
@@ -2802,6 +2836,7 @@ class TangoClient:
2802
2836
  return PaginatedResponse(
2803
2837
  count=data["count"],
2804
2838
  next=data.get("next"),
2839
+ count_type=data.get(_COUNT_TYPE_KEY),
2805
2840
  previous=data.get("previous"),
2806
2841
  results=results,
2807
2842
  meta=data.get("meta"),
@@ -2940,6 +2975,7 @@ class TangoClient:
2940
2975
  return PaginatedResponse(
2941
2976
  count=data["count"],
2942
2977
  next=data.get("next"),
2978
+ count_type=data.get(_COUNT_TYPE_KEY),
2943
2979
  previous=data.get("previous"),
2944
2980
  results=results,
2945
2981
  meta=data.get("meta"),
@@ -3080,6 +3116,7 @@ class TangoClient:
3080
3116
  return PaginatedResponse(
3081
3117
  count=data["count"],
3082
3118
  next=data.get("next"),
3119
+ count_type=data.get(_COUNT_TYPE_KEY),
3083
3120
  previous=data.get("previous"),
3084
3121
  results=results,
3085
3122
  meta=data.get("meta"),
@@ -3288,6 +3325,7 @@ class TangoClient:
3288
3325
  return PaginatedResponse(
3289
3326
  count=data["count"],
3290
3327
  next=data.get("next"),
3328
+ count_type=data.get(_COUNT_TYPE_KEY),
3291
3329
  previous=data.get("previous"),
3292
3330
  results=results,
3293
3331
  meta=data.get("meta"),
@@ -3408,6 +3446,7 @@ class TangoClient:
3408
3446
  return PaginatedResponse(
3409
3447
  count=data["count"],
3410
3448
  next=data.get("next"),
3449
+ count_type=data.get(_COUNT_TYPE_KEY),
3411
3450
  previous=data.get("previous"),
3412
3451
  results=results,
3413
3452
  meta=data.get("meta"),
@@ -3546,6 +3585,7 @@ class TangoClient:
3546
3585
  return PaginatedResponse(
3547
3586
  count=data["count"],
3548
3587
  next=data.get("next"),
3588
+ count_type=data.get(_COUNT_TYPE_KEY),
3549
3589
  previous=data.get("previous"),
3550
3590
  results=results,
3551
3591
  meta=data.get("meta"),
@@ -3695,6 +3735,7 @@ class TangoClient:
3695
3735
  return PaginatedResponse(
3696
3736
  count=data["count"],
3697
3737
  next=data.get("next"),
3738
+ count_type=data.get(_COUNT_TYPE_KEY),
3698
3739
  previous=data.get("previous"),
3699
3740
  results=results,
3700
3741
  meta=data.get("meta"),
@@ -3826,6 +3867,7 @@ class TangoClient:
3826
3867
  return PaginatedResponse(
3827
3868
  count=data["count"],
3828
3869
  next=data.get("next"),
3870
+ count_type=data.get(_COUNT_TYPE_KEY),
3829
3871
  previous=data.get("previous"),
3830
3872
  results=results,
3831
3873
  meta=data.get("meta"),
@@ -3947,6 +3989,7 @@ class TangoClient:
3947
3989
  return PaginatedResponse(
3948
3990
  count=data["count"],
3949
3991
  next=data.get("next"),
3992
+ count_type=data.get(_COUNT_TYPE_KEY),
3950
3993
  previous=data.get("previous"),
3951
3994
  results=results,
3952
3995
  meta=data.get("meta"),
@@ -4155,6 +4198,7 @@ class TangoClient:
4155
4198
  return PaginatedResponse(
4156
4199
  count=data["count"],
4157
4200
  next=data.get("next"),
4201
+ count_type=data.get(_COUNT_TYPE_KEY),
4158
4202
  previous=data.get("previous"),
4159
4203
  results=results,
4160
4204
  meta=data.get("meta"),
@@ -4267,6 +4311,7 @@ class TangoClient:
4267
4311
  return PaginatedResponse(
4268
4312
  count=data["count"],
4269
4313
  next=data.get("next"),
4314
+ count_type=data.get(_COUNT_TYPE_KEY),
4270
4315
  previous=data.get("previous"),
4271
4316
  results=results,
4272
4317
  meta=data.get("meta"),
@@ -4391,6 +4436,7 @@ class TangoClient:
4391
4436
  return PaginatedResponse(
4392
4437
  count=data["count"],
4393
4438
  next=data.get("next"),
4439
+ count_type=data.get(_COUNT_TYPE_KEY),
4394
4440
  previous=data.get("previous"),
4395
4441
  results=results,
4396
4442
  meta=data.get("meta"),
@@ -4792,6 +4838,7 @@ class TangoClient:
4792
4838
  return PaginatedResponse(
4793
4839
  count=data.get("count", 0),
4794
4840
  next=data.get("next"),
4841
+ count_type=data.get(_COUNT_TYPE_KEY),
4795
4842
  previous=data.get("previous"),
4796
4843
  results=results,
4797
4844
  meta=data.get("meta"),
@@ -4844,6 +4891,7 @@ class TangoClient:
4844
4891
  return PaginatedResponse(
4845
4892
  count=int(data.get("count") or len(data.get("results") or [])),
4846
4893
  next=data.get("next"),
4894
+ count_type=data.get(_COUNT_TYPE_KEY),
4847
4895
  previous=data.get("previous"),
4848
4896
  results=data.get("results") or [],
4849
4897
  meta=data.get("meta"),
@@ -4872,6 +4920,7 @@ class TangoClient:
4872
4920
  return PaginatedResponse(
4873
4921
  count=int(data.get("count") or len(data.get("results") or [])),
4874
4922
  next=data.get("next"),
4923
+ count_type=data.get(_COUNT_TYPE_KEY),
4875
4924
  previous=data.get("previous"),
4876
4925
  results=data.get("results") or [],
4877
4926
  meta=data.get("meta"),
@@ -4966,6 +5015,7 @@ class TangoClient:
4966
5015
  return PaginatedResponse(
4967
5016
  count=data["count"],
4968
5017
  next=data.get("next"),
5018
+ count_type=data.get(_COUNT_TYPE_KEY),
4969
5019
  previous=data.get("previous"),
4970
5020
  results=results,
4971
5021
  meta=data.get("meta"),
@@ -5059,6 +5109,7 @@ class TangoClient:
5059
5109
  return PaginatedResponse(
5060
5110
  count=int(data.get("count", len(results))),
5061
5111
  next=data.get("next"),
5112
+ count_type=data.get(_COUNT_TYPE_KEY),
5062
5113
  previous=data.get("previous"),
5063
5114
  results=results,
5064
5115
  meta=data.get("meta"),
@@ -5244,6 +5295,7 @@ class TangoClient:
5244
5295
  return PaginatedResponse(
5245
5296
  count=int(data.get("count", len(results))),
5246
5297
  next=data.get("next"),
5298
+ count_type=data.get(_COUNT_TYPE_KEY),
5247
5299
  previous=data.get("previous"),
5248
5300
  results=results,
5249
5301
  meta=data.get("meta"),
@@ -5462,6 +5514,7 @@ class TangoClient:
5462
5514
  return PaginatedResponse(
5463
5515
  count=int(data.get("count", 0)),
5464
5516
  next=data.get("next"),
5517
+ count_type=data.get(_COUNT_TYPE_KEY),
5465
5518
  previous=data.get("previous"),
5466
5519
  results=list(data.get("results") or []),
5467
5520
  meta=data.get("meta"),
@@ -5500,6 +5553,7 @@ class TangoClient:
5500
5553
  return PaginatedResponse(
5501
5554
  count=int(data.get("count", 0)),
5502
5555
  next=data.get("next"),
5556
+ count_type=data.get(_COUNT_TYPE_KEY),
5503
5557
  previous=data.get("previous"),
5504
5558
  results=list(data.get("results") or []),
5505
5559
  meta=data.get("meta"),
@@ -5601,6 +5655,7 @@ class TangoClient:
5601
5655
  return PaginatedResponse(
5602
5656
  count=int(data.get("count", 0)),
5603
5657
  next=data.get("next"),
5658
+ count_type=data.get(_COUNT_TYPE_KEY),
5604
5659
  previous=data.get("previous"),
5605
5660
  results=list(data.get("results") or []),
5606
5661
  meta=data.get("meta"),
@@ -5648,6 +5703,7 @@ class TangoClient:
5648
5703
  return PaginatedResponse(
5649
5704
  count=int(data.get("count", 0)),
5650
5705
  next=data.get("next"),
5706
+ count_type=data.get(_COUNT_TYPE_KEY),
5651
5707
  previous=data.get("previous"),
5652
5708
  results=list(data.get("results") or []),
5653
5709
  meta=data.get("meta"),
@@ -5716,6 +5772,7 @@ class TangoClient:
5716
5772
  return PaginatedResponse(
5717
5773
  count=int(data.get("count") or len(results)),
5718
5774
  next=data.get("next"),
5775
+ count_type=data.get(_COUNT_TYPE_KEY),
5719
5776
  previous=data.get("previous"),
5720
5777
  results=results,
5721
5778
  cursor=data.get("cursor"),
@@ -5894,6 +5951,7 @@ class TangoClient:
5894
5951
  return PaginatedResponse(
5895
5952
  count=int(data.get("count", 0)),
5896
5953
  next=data.get("next"),
5954
+ count_type=data.get(_COUNT_TYPE_KEY),
5897
5955
  previous=data.get("previous"),
5898
5956
  results=results,
5899
5957
  meta=data.get("meta"),
@@ -5925,6 +5983,7 @@ class TangoClient:
5925
5983
  return PaginatedResponse(
5926
5984
  count=int(data.get("count", 0)),
5927
5985
  next=data.get("next"),
5986
+ count_type=data.get(_COUNT_TYPE_KEY),
5928
5987
  previous=data.get("previous"),
5929
5988
  results=list(data.get("results") or []),
5930
5989
  meta=data.get("meta"),
@@ -5966,6 +6025,7 @@ class TangoClient:
5966
6025
  return PaginatedResponse(
5967
6026
  count=int(data.get("count", 0)),
5968
6027
  next=data.get("next"),
6028
+ count_type=data.get(_COUNT_TYPE_KEY),
5969
6029
  previous=data.get("previous"),
5970
6030
  results=list(data.get("results") or []),
5971
6031
  meta=data.get("meta"),
@@ -6022,6 +6082,7 @@ class TangoClient:
6022
6082
  return PaginatedResponse(
6023
6083
  count=int(data.get("count") or len(results)),
6024
6084
  next=data.get("next"),
6085
+ count_type=data.get(_COUNT_TYPE_KEY),
6025
6086
  previous=data.get("previous"),
6026
6087
  results=results,
6027
6088
  cursor=data.get("cursor"),
tango/models.py CHANGED
@@ -571,6 +571,12 @@ class Opportunity:
571
571
  The award fields (``award_date``, ``award_amount``, ``awardee``, ``awardee_uei``) are filled only where SAM.gov posted an award notice, and ``award_amount`` is the text SAM.gov published, not a number.
572
572
  SAM.gov often posts an award as its own opportunity; when that award notice references its solicitation, ``solicitation_opportunity_id`` points back to it, and the solicitation's ``awards(...)`` expand lists up to ten of the most recent linked awards, with ``award_count`` giving the full count.
573
573
  An award notice that does not reference its solicitation is not linked.
574
+
575
+ The ``meta(...)`` expand counts an opportunity's attachments three ways (Tango API 5.9.0).
576
+ An attachment's ``type`` is ``file`` (a document attached to the notice; only files have extracted text) or ``link`` (a URL the notice lists).
577
+ ``meta.files_count`` and ``meta.links_count`` count each, and ``meta.attachments_count`` counts every attachment, links included.
578
+ An attachment of any other type counts only in the total, so files plus links need not equal it.
579
+ ``files_count`` and ``links_count`` are null until the record has been counted, so treat null as unknown, not zero.
574
580
  """
575
581
 
576
582
  opportunity_id: str
@@ -600,6 +606,10 @@ class Notice:
600
606
  Both must be named, e.g. ``attachments(name,url,doc_role,doc_role_alt)``, because ``attachments(*)`` does not include them.
601
607
  An attachment Tango has not classified omits both keys rather than returning null, so read them with ``.get()``.
602
608
  A Free-plan request that names them gets the response without them, plus an entry in ``meta.upgrade_hints``.
609
+
610
+ ``file_count`` and ``link_count`` (Tango API 5.9.0) split ``attachment_count`` by attachment ``type``: ``file`` is a document attached to the notice (only files have extracted text) and ``link`` is a URL the notice lists.
611
+ ``attachment_count`` still counts every attachment, links included, and an attachment of any other type counts only there, so files plus links need not equal it.
612
+ ``file_count`` and ``link_count`` are null until the notice has been counted, so treat null as unknown, not zero.
603
613
  """
604
614
 
605
615
  notice_id: str
@@ -613,6 +623,9 @@ class Notice:
613
623
  award_amount: str | None = None
614
624
  awardee: str | None = None
615
625
  awardee_uei: str | None = None
626
+ attachment_count: int | None = None
627
+ file_count: int | None = None
628
+ link_count: int | None = None
616
629
 
617
630
 
618
631
  @dataclass
@@ -1454,14 +1467,15 @@ class PaginatedResponse[T]:
1454
1467
  results: List of result items (type depends on shape parameter)
1455
1468
  cursor: Cursor token for cursor-based pagination (None if not available)
1456
1469
  meta: Response-level metadata the API attached to this page, when present.
1457
- Currently carries agency-filter diagnostics: ``resolved_filters`` maps
1458
- each agency filter to the organizations its ``|``-separated tokens
1459
- resolved to (or ``None``), and ``warnings`` lists human-readable notes
1460
- about tokens that were dropped or matched loosely. See
1461
- :meth:`agency_warnings` and :meth:`unresolved_agency_tokens`.
1470
+ Currently carries agency-filter diagnostics: ``resolved_filters`` maps each agency filter to the organizations its ``|``-separated tokens resolved to (or ``None``), and ``warnings`` lists human-readable notes about tokens that were dropped or matched loosely.
1471
+ Each ``resolved_filters`` entry that resolved also carries ``matched_by`` (Tango API 5.9.0+), saying how the token matched: ``"key"`` (an organization UUID), ``"code"`` (a 3-digit CGAC or 4-digit FPDS code), ``"name"`` (the organization's name, including a department's everyday name, a spelling variant or a rename), ``"alias"`` (an abbreviation or the organization's own alias) or ``"fuzzy"`` (a looser text match, worth checking against the resolved ``name``).
1472
+ An entry that did not resolve has no ``matched_by``.
1473
+ See :meth:`agency_warnings`, :meth:`unresolved_agency_tokens` and :meth:`resolved_agencies`.
1462
1474
  page_metadata: Always ``None`` — the API has never emitted a
1463
1475
  ``page_metadata`` key. Retained so existing attribute access keeps
1464
1476
  working; use ``meta`` instead.
1477
+ count_type: ``"exact"`` or ``"approximate"``, from the API's ``X-Results-CountType`` header; ``None`` when the endpoint doesn't send it.
1478
+ The contract, IDV, opportunity and notice lists can switch to a query-planner estimate past 1,000 matches, which can be well off.
1465
1479
 
1466
1480
  Examples:
1467
1481
  >>> from tango import TangoClient, ShapeConfig
@@ -1482,6 +1496,7 @@ class PaginatedResponse[T]:
1482
1496
  cursor: str | None = None
1483
1497
  meta: dict[str, Any] | None = None
1484
1498
  page_metadata: dict[str, Any] | None = None
1499
+ count_type: str | None = None
1485
1500
 
1486
1501
  @property
1487
1502
  def agency_warnings(self) -> list[str]:
@@ -641,6 +641,8 @@ META2_SCHEMA: dict[str, FieldSchema] = {
641
641
  "attachments_count": FieldSchema(
642
642
  name="attachments_count", type=int, is_optional=True, is_list=False
643
643
  ),
644
+ "files_count": FieldSchema(name="files_count", type=int, is_optional=True, is_list=False),
645
+ "links_count": FieldSchema(name="links_count", type=int, is_optional=True, is_list=False),
644
646
  "notice_type": FieldSchema(
645
647
  name="notice_type", type=dict, is_optional=True, is_list=False, nested_model="NoticeType"
646
648
  ),
@@ -2252,6 +2254,8 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2252
2254
  "award_date": FieldSchema(name="award_date", type=date, is_optional=True, is_list=False),
2253
2255
  "awardee": FieldSchema(name="awardee", type=str, is_optional=True, is_list=False),
2254
2256
  "awardee_uei": FieldSchema(name="awardee_uei", type=str, is_optional=True, is_list=False),
2257
+ "file_count": FieldSchema(name="file_count", type=int, is_optional=True, is_list=False),
2258
+ "link_count": FieldSchema(name="link_count", type=int, is_optional=True, is_list=False),
2255
2259
  "meta": FieldSchema(
2256
2260
  name="meta", type=dict, is_optional=True, is_list=False, nested_model="Meta"
2257
2261
  ),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tango-python
3
- Version: 1.13.0
3
+ Version: 1.15.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
@@ -219,6 +219,15 @@ for warning in response.agency_warnings:
219
219
  print(warning)
220
220
  ```
221
221
 
222
+ Each entry of `response.meta["resolved_filters"][<filter name>]` that resolved also says how its token matched, in `matched_by` (Tango API 5.9.0+): `key` (an organization UUID), `code` (a 3-digit CGAC or 4-digit FPDS code), `name` (the organization's name, including a department's everyday name, a spelling variant or a rename), `alias` (an abbreviation or the organization's own alias) or `fuzzy` (a looser text match).
223
+ A `fuzzy` match is the one worth checking against the resolved name:
224
+
225
+ ```python
226
+ for entry in response.meta["resolved_filters"].get("awarding_agency", []):
227
+ if entry.get("matched_by") == "fuzzy":
228
+ print(f"{entry['token']!r} loosely matched {entry['resolved']['name']}")
229
+ ```
230
+
222
231
  If *every* token for a filter fails to resolve, the API returns `400` and the SDK raises
223
232
  `TangoValidationError` naming the offending value, rather than an empty page.
224
233
 
@@ -1,11 +1,11 @@
1
- tango/__init__.py,sha256=L5_oz-laG934CX8HE8EOPEPW1a7JHmXo7dsTLwCGC5c,2739
2
- tango/client.py,sha256=N-EKUT2OquRrjpInxK0ZnWVTuDP5ylEXadshjvxO6ng,240281
1
+ tango/__init__.py,sha256=UuWXzqRhKa6JLxJDbNyApeszFOGrskPWRTn_z6XykCM,2739
2
+ tango/client.py,sha256=1Z748iPEPdVi8_Sq9XPES8RnPdnugEwL3yAgV7Cvvt0,243419
3
3
  tango/exceptions.py,sha256=oPiLCccBoWLK5SjHkzP-oI6mUD9twEeKSvEuLV5h01I,4224
4
- tango/models.py,sha256=w7z49WIMcvvEt05J1DpuUPcFWBq_MbCWa4a-f-YLsVs,69936
4
+ tango/models.py,sha256=M8HmbbnvrUcnHoPU7sJn2UtPu6h9UPzWAwQf53LEUAo,72006
5
5
  tango/shapes/__init__.py,sha256=7ea1WU74jp4znhNw-gXruag6m6eyPZtbVgbDFmFUWro,1072
6
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=fyCgKDueIKUcTMtij793mtJcAsC8RynMhU5FXI_9hG0,140860
8
+ tango/shapes/generated_overlay.py,sha256=7UwqTeqNW4NIuwsQlKVZgBlH2biEVBdEZ4z2IwiS1k4,141244
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.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,,
19
+ tango_python-1.15.0.dist-info/METADATA,sha256=gCAx5F4Z_NnqK1CTR4uLgMcpwbKAiZZizruqonN8B_o,22824
20
+ tango_python-1.15.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
21
+ tango_python-1.15.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
22
+ tango_python-1.15.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
23
+ tango_python-1.15.0.dist-info/RECORD,,