tango-python 1.14.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.14.0"
65
+ __version__ = "1.15.0"
66
66
  __all__ = [
67
67
  "TangoClient",
68
68
  "TangoAPIError",
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,11 +1467,10 @@ 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.
@@ -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.14.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=XZv0Q3i4cb0rMu4DTMvB6vthWIQsgfmgnTt5EVnqdac,2739
1
+ tango/__init__.py,sha256=UuWXzqRhKa6JLxJDbNyApeszFOGrskPWRTn_z6XykCM,2739
2
2
  tango/client.py,sha256=1Z748iPEPdVi8_Sq9XPES8RnPdnugEwL3yAgV7Cvvt0,243419
3
3
  tango/exceptions.py,sha256=oPiLCccBoWLK5SjHkzP-oI6mUD9twEeKSvEuLV5h01I,4224
4
- tango/models.py,sha256=Is7HRH3xLy8k82aeRqaJU6zaxZ7CSui4D5Pg9q7Pr9Y,70257
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.14.0.dist-info/METADATA,sha256=IMQQV5MA3ekEPPGbdTWW6K0eYMxCXIzarR5DSM3HxZQ,22118
20
- tango_python-1.14.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
21
- tango_python-1.14.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
22
- tango_python-1.14.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
23
- tango_python-1.14.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,,