tango-python 1.6.0__py3-none-any.whl → 1.7.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
@@ -10,6 +10,7 @@ from .exceptions import (
10
10
  )
11
11
  from .models import (
12
12
  BudgetAccount,
13
+ ContractAppeal,
13
14
  DibbsAward,
14
15
  DibbsRfp,
15
16
  DibbsRfq,
@@ -53,7 +54,7 @@ from .webhooks import (
53
54
  )
54
55
  from .webhooks.receiver import Delivery, WebhookReceiver
55
56
 
56
- __version__ = "1.6.0"
57
+ __version__ = "1.7.0"
57
58
  __all__ = [
58
59
  "TangoClient",
59
60
  "TangoAPIError",
@@ -65,6 +66,7 @@ __all__ = [
65
66
  "ResolveCandidate",
66
67
  "ResolveResult",
67
68
  "BudgetAccount",
69
+ "ContractAppeal",
68
70
  "DibbsAward",
69
71
  "DibbsRfp",
70
72
  "DibbsRfq",
tango/client.py CHANGED
@@ -24,6 +24,7 @@ from tango.models import (
24
24
  BudgetAccount,
25
25
  BusinessType,
26
26
  Contract,
27
+ ContractAppeal,
27
28
  DibbsAward,
28
29
  DibbsRfp,
29
30
  DibbsRfq,
@@ -2592,18 +2593,18 @@ class TangoClient:
2592
2593
  shape: Response shape string (defaults to minimal shape)
2593
2594
  flat: If True, flatten nested objects in shaped response
2594
2595
  flat_lists: If True, flatten arrays using indexed keys
2595
- source_system: Filter by source system (e.g. gao)
2596
+ source_system: Filter by source system: gao, cofc, or sba_oha
2596
2597
  outcome: Filter by outcome (e.g. Denied, Dismissed, Withdrawn, Sustained)
2597
2598
  case_type: Filter by case type
2598
2599
  agency: Filter by protested agency text
2599
- case_number: Filter by case number (e.g. b-423274)
2600
+ case_number: Filter by base case number, matched case-insensitively (e.g. b-423274 for GAO, 26-1391 for COFC, SIZ-6100 for SBA OHA)
2600
2601
  solicitation_number: Filter by exact solicitation number
2601
2602
  protester: Filter by protester name text
2602
2603
  filed_date_after: Filed date on or after
2603
2604
  filed_date_before: Filed date on or before
2604
2605
  decision_date_after: Decision date on or after
2605
2606
  decision_date_before: Decision date on or before
2606
- naics_code: Filter by the solicitation's NAICS code (e.g. 541519)
2607
+ naics_code: Filter by the NAICS code at issue in an SBA OHA size or NAICS appeal (e.g. 541519); GAO and COFC protests carry no NAICS code and never match
2607
2608
  search: Full-text search over protest searchable fields
2608
2609
  """
2609
2610
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
@@ -2682,6 +2683,141 @@ class TangoClient:
2682
2683
  data = self._get(f"/api/protests/{case_id}/", params)
2683
2684
  return self._parse_response_with_shape(data, shape, Protest, flat, flat_lists)
2684
2685
 
2686
+ # ============================================================================
2687
+ # Contract Disputes Act appeals (CBCA and ASBCA)
2688
+ # ============================================================================
2689
+
2690
+ def list_contract_appeals(
2691
+ self,
2692
+ page: int = 1,
2693
+ limit: int = 25,
2694
+ shape: str | None = None,
2695
+ flat: bool = False,
2696
+ flat_lists: bool = False,
2697
+ board: str | None = None,
2698
+ docket: str | None = None,
2699
+ appellant: str | None = None,
2700
+ judge: str | None = None,
2701
+ decision_type: str | None = None,
2702
+ decision_date_after: str | None = None,
2703
+ decision_date_before: str | None = None,
2704
+ listed: bool | None = None,
2705
+ document_id: str | None = None,
2706
+ search: str | None = None,
2707
+ ordering: str | None = None,
2708
+ ) -> PaginatedResponse:
2709
+ """
2710
+ List Contract Disputes Act appeal decisions.
2711
+
2712
+ Decisions of the Civilian Board of Contract Appeals (CBCA) and the Armed
2713
+ Services Board of Contract Appeals (ASBCA) — appeals of a contracting
2714
+ officer's decision, which is a different dispute from a bid protest. For
2715
+ protests, use :meth:`list_protests`.
2716
+
2717
+ Args:
2718
+ page: Page number
2719
+ limit: Results per page (max 100)
2720
+ shape: Response shape string (defaults to minimal shape)
2721
+ flat: If True, flatten nested objects in shaped response
2722
+ flat_lists: If True, flatten arrays using indexed keys
2723
+ board: ``cbca`` or ``asbca``. Multi-value: ``cbca|asbca``
2724
+ docket: Exact docket match, e.g. ``3288-R``, ``59116`` or
2725
+ ``7092-C(6682, 6765, 6767)``. A leading ``CBCA`` / ``ASBCA`` and
2726
+ ``No.`` / ``Nos.`` are ignored, so a docket can be pasted as it is
2727
+ cited. Commas are part of a docket, never a separator; OR several
2728
+ with ``|``
2729
+ appellant: Case-insensitive substring match on the appellant's name
2730
+ (min 2 characters)
2731
+ judge: Case-insensitive exact match on the judge as the listing names
2732
+ them
2733
+ decision_type: CBCA only — ``Decision``, ``Dismissal``, ``Order``,
2734
+ ``Full Board Order``, or the listing's own text where it matches
2735
+ none of those. ASBCA rows carry no type
2736
+ decision_date_after: Decided on or after (YYYY-MM-DD)
2737
+ decision_date_before: Decided on or before (YYYY-MM-DD)
2738
+ listed: Whether the board's newest listing still carries the decision.
2739
+ ``False`` returns the ones it has dropped, which are kept here
2740
+ rather than deleted
2741
+ document_id: The board's own document id
2742
+ search: Ranked full-text search over the appellant and the full
2743
+ decision text (min 2 characters). Wrap in double quotes for a
2744
+ phrase. Free at every plan, and it returns no fragment of the
2745
+ text — only reading ``decision_text`` needs Enterprise
2746
+ ordering: Sort field — ``decision_date`` (the default, as
2747
+ ``-decision_date``), ``appellant``, ``first_listed_at`` or
2748
+ ``rank``. ``rank`` requires a non-empty ``search``
2749
+ """
2750
+ params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
2751
+
2752
+ if shape is None:
2753
+ shape = ShapeConfig.CONTRACT_APPEALS_MINIMAL
2754
+ if shape:
2755
+ params["shape"] = shape
2756
+ if flat:
2757
+ params["flat"] = "true"
2758
+ if flat_lists:
2759
+ params["flat_lists"] = "true"
2760
+
2761
+ for key, val in (
2762
+ ("board", board),
2763
+ ("docket", docket),
2764
+ ("appellant", appellant),
2765
+ ("judge", judge),
2766
+ ("decision_type", decision_type),
2767
+ ("decision_date_after", decision_date_after),
2768
+ ("decision_date_before", decision_date_before),
2769
+ ("listed", listed),
2770
+ ("document_id", document_id),
2771
+ ("search", search),
2772
+ ("ordering", ordering),
2773
+ ):
2774
+ if val is not None:
2775
+ params[key] = val
2776
+
2777
+ data = self._get("/api/contract_appeals/", params)
2778
+
2779
+ results = [
2780
+ self._parse_response_with_shape(item, shape, ContractAppeal, flat, flat_lists)
2781
+ for item in data["results"]
2782
+ ]
2783
+
2784
+ return PaginatedResponse(
2785
+ count=data["count"],
2786
+ next=data.get("next"),
2787
+ previous=data.get("previous"),
2788
+ results=results,
2789
+ meta=data.get("meta"),
2790
+ )
2791
+
2792
+ def get_contract_appeal(
2793
+ self,
2794
+ appeal_id: str,
2795
+ shape: str | None = None,
2796
+ flat: bool = False,
2797
+ flat_lists: bool = False,
2798
+ ) -> Any:
2799
+ """
2800
+ Get a single Contract Disputes Act appeal decision by uuid.
2801
+
2802
+ Args:
2803
+ appeal_id: Decision UUID
2804
+ shape: Response shape string (defaults to the comprehensive shape)
2805
+ flat: If True, flatten nested objects in shaped response
2806
+ flat_lists: If True, flatten arrays using indexed keys
2807
+ """
2808
+ params: dict[str, Any] = {}
2809
+ if shape is None:
2810
+ shape = ShapeConfig.CONTRACT_APPEALS_COMPREHENSIVE
2811
+ if shape:
2812
+ params["shape"] = shape
2813
+ if flat:
2814
+ params["flat"] = "true"
2815
+ if flat_lists:
2816
+ params["flat_lists"] = "true"
2817
+
2818
+ data = self._get(f"/api/contract_appeals/{appeal_id}/", params)
2819
+ return self._parse_response_with_shape(data, shape, ContractAppeal, flat, flat_lists)
2820
+
2685
2821
  # ============================================================================
2686
2822
  # DLA DIBBS (RFQs, RFPs, awards)
2687
2823
  # ============================================================================
@@ -4733,7 +4869,10 @@ class TangoClient:
4733
4869
  Args:
4734
4870
  name: Human-readable name for this alert.
4735
4871
  query_type: One of ``opportunity``, ``contract``, ``idv``, ``ota``,
4736
- ``otidv``, ``entity``, ``grant``, ``forecast``.
4872
+ ``otidv``, ``entity``, ``grant``, ``forecast``, ``exclusion``,
4873
+ ``dibbs_rfq``, ``dibbs_rfp``, ``sled_opportunity`` or
4874
+ ``contract_appeal``. :meth:`list_webhook_event_types` is the
4875
+ current authority — this list can only go stale.
4737
4876
  filters: Dict of query parameters that the alert matches against
4738
4877
  (e.g. ``{"naics": "541330", "set_aside": "SBA"}``).
4739
4878
  frequency: ``realtime`` | ``daily`` | ``weekly`` | ``custom``.
tango/models.py CHANGED
@@ -559,7 +559,13 @@ class Forecast:
559
559
 
560
560
  @dataclass
561
561
  class Opportunity:
562
- """Schema definition for Opportunity (not used for instances)"""
562
+ """Schema definition for Opportunity (not used for instances)
563
+
564
+ The ``attachments(...)`` expand can carry two document-role fields on a Pro plan or above: ``doc_role``, what the document is for (one of ``requirement``, ``instructions``, ``pricing``, ``terms``, ``reference``, ``unknown``), and ``doc_role_alt``, a runner-up role that is usually null.
565
+ Both must be named, e.g. ``attachments(name,url,doc_role,doc_role_alt)``, because ``attachments(*)`` does not include them.
566
+ An attachment Tango has not classified omits both keys rather than returning null, so read them with ``.get()``.
567
+ A Free-plan request that names them gets the response without them, plus an entry in ``meta.upgrade_hints``.
568
+ """
563
569
 
564
570
  opportunity_id: str
565
571
  title: str
@@ -573,7 +579,13 @@ class Opportunity:
573
579
 
574
580
  @dataclass
575
581
  class Notice:
576
- """Schema definition for Notice (not used for instances)"""
582
+ """Schema definition for Notice (not used for instances)
583
+
584
+ The ``attachments(...)`` expand can carry two document-role fields on a Pro plan or above: ``doc_role``, what the document is for (one of ``requirement``, ``instructions``, ``pricing``, ``terms``, ``reference``, ``unknown``), and ``doc_role_alt``, a runner-up role that is usually null.
585
+ Both must be named, e.g. ``attachments(name,url,doc_role,doc_role_alt)``, because ``attachments(*)`` does not include them.
586
+ An attachment Tango has not classified omits both keys rather than returning null, so read them with ``.get()``.
587
+ A Free-plan request that names them gets the response without them, plus an entry in ``meta.upgrade_hints``.
588
+ """
577
589
 
578
590
  notice_id: str
579
591
  title: str
@@ -1050,6 +1062,10 @@ class SledOpportunity:
1050
1062
  Searching document text and reading it are separate: ``search=`` matches
1051
1063
  inside attachment text on every plan and returns no fragment of it, while
1052
1064
  ``attachments(extracted_text)`` serves the body on a Small plan or above.
1065
+
1066
+ ``delisted_at`` is when a portal dropped the solicitation from its listing before its deadline, which is the reading behind ``status_reason="delisted"``; it is null when the solicitation was never delisted or has been seen again since.
1067
+
1068
+ ``meta(jurisdiction_declared)`` is a boolean saying whether the source stated the jurisdiction level itself; ``False`` means Tango classified it from the issuer's name.
1053
1069
  """
1054
1070
 
1055
1071
  opportunity_id: str | None = None
@@ -1063,6 +1079,7 @@ class SledOpportunity:
1063
1079
  agency: str | None = None
1064
1080
  status: str | None = None
1065
1081
  status_reason: str | None = None
1082
+ delisted_at: str | None = None
1066
1083
  status_computed_at: str | None = None
1067
1084
  source_status: str | None = None
1068
1085
  source_url: str | None = None
@@ -1151,6 +1168,51 @@ class SledForecast:
1151
1168
  raw: dict[str, Any] | None = None
1152
1169
 
1153
1170
 
1171
+ class ContractAppeal:
1172
+ """Schema definition for ContractAppeal (not used for instances).
1173
+
1174
+ Contract Disputes Act appeal decisions at ``/api/contract_appeals/``, from the
1175
+ Civilian Board of Contract Appeals (CBCA) and the Armed Services Board of
1176
+ Contract Appeals (ASBCA). These are appeals of a contracting officer's
1177
+ decision, not bid protests — protests live at ``/api/protests/``.
1178
+ Shape-on-demand, so every field is optional.
1179
+
1180
+ One row is one decision as the board's own listing publishes it, so several
1181
+ fields describe the listing rather than the dispute. ``listed`` goes false
1182
+ once the board's newest listing stops carrying the decision; the row itself is
1183
+ kept, never dropped. ``docket_numbers`` holds the dockets with the board
1184
+ prefix stripped (``3288-R``, ``59116``) and a consolidated appeal carries
1185
+ several, while ``docket_raw`` keeps the listing's own text.
1186
+
1187
+ ``decision_text`` is the extracted decision body and needs an Enterprise plan.
1188
+ Below that tier the key is ABSENT rather than null, so read it with ``.get()``.
1189
+ Searching that text is free at every plan: ``search=`` matches inside it and
1190
+ returns no fragment of it.
1191
+ """
1192
+
1193
+ uuid: str | None = None
1194
+ board: str | None = None
1195
+ docket_numbers: list[str] | None = None
1196
+ docket_source: str | None = None
1197
+ docket_raw: str | None = None
1198
+ decision_date: date | None = None
1199
+ decision_date_raw: str | None = None
1200
+ decision_date_repaired: bool | None = None
1201
+ appellant: str | None = None
1202
+ judge: str | None = None
1203
+ decision_type: str | None = None
1204
+ decision_type_raw: str | None = None
1205
+ url: str | None = None
1206
+ document_id: str | None = None
1207
+ listing_url: str | None = None
1208
+ listing_year: int | None = None
1209
+ first_listed_at: datetime | None = None
1210
+ listed: bool | None = None
1211
+ text_status: str | None = None
1212
+ text_char_count: int | None = None
1213
+ decision_text: str | None = None
1214
+
1215
+
1154
1216
  @dataclass
1155
1217
  class PaginatedResponse[T]:
1156
1218
  """Paginated API response
@@ -1317,6 +1379,21 @@ class ShapeConfig:
1317
1379
  # Default for list_protests()
1318
1380
  PROTESTS_MINIMAL: Final = "case_id,case_number,title,source_system,outcome,filed_date"
1319
1381
 
1382
+ # Default for list_contract_appeals(). Mirrors the API's own list default.
1383
+ CONTRACT_APPEALS_MINIMAL: Final = (
1384
+ "uuid,board,docket_numbers,decision_date,appellant,judge,decision_type,url"
1385
+ )
1386
+
1387
+ # Default for get_contract_appeal(). Mirrors the API's own retrieve default.
1388
+ # `decision_text` is left out on purpose: it runs to ~100K characters and
1389
+ # needs an Enterprise plan, so naming it by default would make every detail
1390
+ # fetch ask for a body most callers cannot read.
1391
+ CONTRACT_APPEALS_COMPREHENSIVE: Final = (
1392
+ "uuid,board,docket_numbers,decision_date,appellant,judge,decision_type,url,"
1393
+ "docket_raw,docket_source,decision_date_repaired,decision_type_raw,"
1394
+ "listing_year,first_listed_at,listed,text_status,text_char_count"
1395
+ )
1396
+
1320
1397
  # Default for list_dibbs_rfqs()
1321
1398
  DIBBS_RFQS_MINIMAL: Final = (
1322
1399
  "uuid,solicitation,nsn,part_number,nomenclature,quantity,issue_date,return_by_date,is_open"
@@ -1356,8 +1433,9 @@ class ShapeConfig:
1356
1433
  # API (median ~550 chars, tail past 120k), so it is not in the list default.
1357
1434
  SLED_OPPORTUNITIES_MINIMAL: Final = (
1358
1435
  "opportunity_id,solicitation_number,solicitation_type,title,state,"
1359
- "jurisdiction,agency,status,status_reason,posted_date,response_deadline,"
1360
- "source_url,has_documents,first_seen_at,last_change_seen_at"
1436
+ "jurisdiction,agency,status,status_reason,delisted_at,posted_date,"
1437
+ "response_deadline,source_url,has_documents,first_seen_at,"
1438
+ "last_change_seen_at"
1361
1439
  )
1362
1440
 
1363
1441
  # Default for get_sled_opportunity(). `attachments(*)` deliberately does not
@@ -1367,8 +1445,8 @@ class ShapeConfig:
1367
1445
  SLED_OPPORTUNITIES_COMPREHENSIVE: Final = (
1368
1446
  "opportunity_id,solicitation_number,solicitation_type,"
1369
1447
  "solicitation_type_source,title,description,state,jurisdiction,agency,"
1370
- "status,status_reason,status_computed_at,source_status,source_url,"
1371
- "posted_date,response_deadline,response_deadline_original,"
1448
+ "status,status_reason,delisted_at,status_computed_at,source_status,"
1449
+ "source_url,posted_date,response_deadline,response_deadline_original,"
1372
1450
  "bid_opening_date,bid_opening_raw,category_codes,has_documents,"
1373
1451
  "first_seen_at,last_seen_at,last_change_seen_at,"
1374
1452
  "organization(*),contact(*),meta(*),attachments(*),revisions(*)"
@@ -763,6 +763,43 @@ PROTEST_SCHEMA: dict[str, FieldSchema] = {
763
763
  }
764
764
 
765
765
 
766
+ CONTRACT_APPEAL_SCHEMA: dict[str, FieldSchema] = {
767
+ "uuid": FieldSchema(name="uuid", type=str, is_optional=True, is_list=False),
768
+ "board": FieldSchema(name="board", type=str, is_optional=True, is_list=False),
769
+ "docket_numbers": FieldSchema(name="docket_numbers", type=str, is_optional=True, is_list=True),
770
+ "docket_source": FieldSchema(name="docket_source", type=str, is_optional=True, is_list=False),
771
+ "docket_raw": FieldSchema(name="docket_raw", type=str, is_optional=True, is_list=False),
772
+ "decision_date": FieldSchema(name="decision_date", type=date, is_optional=True, is_list=False),
773
+ "decision_date_raw": FieldSchema(
774
+ name="decision_date_raw", type=str, is_optional=True, is_list=False
775
+ ),
776
+ "decision_date_repaired": FieldSchema(
777
+ name="decision_date_repaired", type=bool, is_optional=True, is_list=False
778
+ ),
779
+ "appellant": FieldSchema(name="appellant", type=str, is_optional=True, is_list=False),
780
+ "judge": FieldSchema(name="judge", type=str, is_optional=True, is_list=False),
781
+ "decision_type": FieldSchema(name="decision_type", type=str, is_optional=True, is_list=False),
782
+ "decision_type_raw": FieldSchema(
783
+ name="decision_type_raw", type=str, is_optional=True, is_list=False
784
+ ),
785
+ "url": FieldSchema(name="url", type=str, is_optional=True, is_list=False),
786
+ "document_id": FieldSchema(name="document_id", type=str, is_optional=True, is_list=False),
787
+ "listing_url": FieldSchema(name="listing_url", type=str, is_optional=True, is_list=False),
788
+ "listing_year": FieldSchema(name="listing_year", type=int, is_optional=True, is_list=False),
789
+ "first_listed_at": FieldSchema(
790
+ name="first_listed_at", type=datetime, is_optional=True, is_list=False
791
+ ),
792
+ "listed": FieldSchema(name="listed", type=bool, is_optional=True, is_list=False),
793
+ "text_status": FieldSchema(name="text_status", type=str, is_optional=True, is_list=False),
794
+ "text_char_count": FieldSchema(
795
+ name="text_char_count", type=int, is_optional=True, is_list=False
796
+ ),
797
+ # Enterprise-only. The key is absent rather than null below that tier, so the
798
+ # schema still carries it — the SDK must not reject a shape the API accepts.
799
+ "decision_text": FieldSchema(name="decision_text", type=str, is_optional=True, is_list=False),
800
+ }
801
+
802
+
766
803
  AGENCY_SCHEMA: dict[str, FieldSchema] = {
767
804
  "abbreviation": FieldSchema(name="abbreviation", type=str, is_optional=True, is_list=False),
768
805
  "code": FieldSchema(name="code", type=str, is_optional=False, is_list=False),
@@ -1507,6 +1544,7 @@ EXPLICIT_SCHEMAS: dict[str, dict[str, FieldSchema]] = {
1507
1544
  "Notice": NOTICE_SCHEMA,
1508
1545
  "Protest": PROTEST_SCHEMA,
1509
1546
  "ProtestDocket": PROTEST_DOCKET_SCHEMA,
1547
+ "ContractAppeal": CONTRACT_APPEAL_SCHEMA,
1510
1548
  "Agency": AGENCY_SCHEMA,
1511
1549
  "Grant": GRANT_SCHEMA,
1512
1550
  # Vehicles (Awards)
@@ -81,7 +81,20 @@ ARCHIVE_SCHEMA: dict[str, FieldSchema] = {
81
81
  }
82
82
 
83
83
  ATTACHMENTS_SCHEMA: dict[str, FieldSchema] = {
84
+ "doc_name": FieldSchema(name="doc_name", type=str, is_optional=True, is_list=False),
85
+ "doc_path": FieldSchema(name="doc_path", type=str, is_optional=True, is_list=False),
86
+ "doc_seq_num": FieldSchema(name="doc_seq_num", type=str, is_optional=True, is_list=False),
87
+ "doc_session_date": FieldSchema(
88
+ name="doc_session_date", type=date, is_optional=True, is_list=False
89
+ ),
90
+ "doc_type": FieldSchema(name="doc_type", type=str, is_optional=True, is_list=False),
91
+ "is_link": FieldSchema(name="is_link", type=bool, is_optional=True, is_list=False),
92
+ }
93
+
94
+ ATTACHMENTS2_SCHEMA: dict[str, FieldSchema] = {
84
95
  "attachment_id": FieldSchema(name="attachment_id", type=str, is_optional=True, is_list=False),
96
+ "doc_role": FieldSchema(name="doc_role", type=str, is_optional=True, is_list=False),
97
+ "doc_role_alt": FieldSchema(name="doc_role_alt", type=str, is_optional=True, is_list=False),
85
98
  "extracted_text": FieldSchema(name="extracted_text", type=str, is_optional=True, is_list=False),
86
99
  "file_size": FieldSchema(name="file_size", type=int, is_optional=True, is_list=False),
87
100
  "mime_type": FieldSchema(name="mime_type", type=str, is_optional=True, is_list=False),
@@ -92,8 +105,10 @@ ATTACHMENTS_SCHEMA: dict[str, FieldSchema] = {
92
105
  "url": FieldSchema(name="url", type=str, is_optional=True, is_list=False),
93
106
  }
94
107
 
95
- ATTACHMENTS2_SCHEMA: dict[str, FieldSchema] = {
108
+ ATTACHMENTS3_SCHEMA: dict[str, FieldSchema] = {
96
109
  "attachment_id": FieldSchema(name="attachment_id", type=str, is_optional=True, is_list=False),
110
+ "doc_role": FieldSchema(name="doc_role", type=str, is_optional=True, is_list=False),
111
+ "doc_role_alt": FieldSchema(name="doc_role_alt", type=str, is_optional=True, is_list=False),
97
112
  "extracted_text": FieldSchema(name="extracted_text", type=str, is_optional=True, is_list=False),
98
113
  "file_size": FieldSchema(name="file_size", type=int, is_optional=True, is_list=False),
99
114
  "mime_type": FieldSchema(name="mime_type", type=str, is_optional=True, is_list=False),
@@ -104,7 +119,7 @@ ATTACHMENTS2_SCHEMA: dict[str, FieldSchema] = {
104
119
  "url": FieldSchema(name="url", type=str, is_optional=True, is_list=False),
105
120
  }
106
121
 
107
- ATTACHMENTS3_SCHEMA: dict[str, FieldSchema] = {
122
+ ATTACHMENTS4_SCHEMA: dict[str, FieldSchema] = {
108
123
  "char_count": FieldSchema(name="char_count", type=int, is_optional=True, is_list=False),
109
124
  "checksum": FieldSchema(name="checksum", type=str, is_optional=True, is_list=False),
110
125
  "download_status": FieldSchema(
@@ -124,13 +139,26 @@ ATTACHMENTS3_SCHEMA: dict[str, FieldSchema] = {
124
139
  ),
125
140
  "mime_type": FieldSchema(name="mime_type", type=str, is_optional=True, is_list=False),
126
141
  "name": FieldSchema(name="name", type=str, is_optional=True, is_list=False),
127
- "pages": FieldSchema(name="pages", type=str, is_optional=True, is_list=False),
128
- "size_bytes": FieldSchema(name="size_bytes", type=str, is_optional=True, is_list=False),
142
+ "pages": FieldSchema(name="pages", type=int, is_optional=True, is_list=False),
143
+ "size_bytes": FieldSchema(name="size_bytes", type=int, is_optional=True, is_list=False),
129
144
  "word_count": FieldSchema(name="word_count", type=int, is_optional=True, is_list=False),
130
145
  }
131
146
 
132
- ATTACHMENTS4_SCHEMA: dict[str, FieldSchema] = {
147
+ ATTACHMENTS5_SCHEMA: dict[str, FieldSchema] = {
133
148
  "attachment_id": FieldSchema(name="attachment_id", type=str, is_optional=True, is_list=False),
149
+ "file_size": FieldSchema(name="file_size", type=str, is_optional=True, is_list=False),
150
+ "mime_type": FieldSchema(name="mime_type", type=str, is_optional=True, is_list=False),
151
+ "name": FieldSchema(name="name", type=str, is_optional=True, is_list=False),
152
+ "posted_date": FieldSchema(name="posted_date", type=date, is_optional=True, is_list=False),
153
+ "resource_id": FieldSchema(name="resource_id", type=str, is_optional=True, is_list=False),
154
+ "type": FieldSchema(name="type", type=str, is_optional=True, is_list=False),
155
+ "url": FieldSchema(name="url", type=str, is_optional=True, is_list=False),
156
+ }
157
+
158
+ ATTACHMENTS6_SCHEMA: dict[str, FieldSchema] = {
159
+ "attachment_id": FieldSchema(name="attachment_id", type=str, is_optional=True, is_list=False),
160
+ "doc_role": FieldSchema(name="doc_role", type=str, is_optional=True, is_list=False),
161
+ "doc_role_alt": FieldSchema(name="doc_role_alt", type=str, is_optional=True, is_list=False),
134
162
  "extracted_text": FieldSchema(name="extracted_text", type=str, is_optional=True, is_list=False),
135
163
  "file_size": FieldSchema(name="file_size", type=str, is_optional=True, is_list=False),
136
164
  "mime_type": FieldSchema(name="mime_type", type=str, is_optional=True, is_list=False),
@@ -375,8 +403,8 @@ DOCUMENTS_SCHEMA: dict[str, FieldSchema] = {
375
403
  }
376
404
 
377
405
  ESTIMATED_VALUE_SCHEMA: dict[str, FieldSchema] = {
378
- "max": FieldSchema(name="max", type=str, is_optional=True, is_list=False),
379
- "min": FieldSchema(name="min", type=str, is_optional=True, is_list=False),
406
+ "max": FieldSchema(name="max", type=Decimal, is_optional=True, is_list=False),
407
+ "min": FieldSchema(name="min", type=Decimal, is_optional=True, is_list=False),
380
408
  "raw": FieldSchema(name="raw", type=str, is_optional=True, is_list=False),
381
409
  }
382
410
 
@@ -624,8 +652,11 @@ META3_SCHEMA: dict[str, FieldSchema] = {
624
652
  "attachment_count": FieldSchema(
625
653
  name="attachment_count", type=int, is_optional=True, is_list=False
626
654
  ),
655
+ "jurisdiction_declared": FieldSchema(
656
+ name="jurisdiction_declared", type=bool, is_optional=True, is_list=False
657
+ ),
627
658
  "last_change_source_declared": FieldSchema(
628
- name="last_change_source_declared", type=str, is_optional=True, is_list=False
659
+ name="last_change_source_declared", type=bool, is_optional=True, is_list=False
629
660
  ),
630
661
  "last_revision_kind": FieldSchema(
631
662
  name="last_revision_kind", type=str, is_optional=True, is_list=False
@@ -942,13 +973,13 @@ RESOLVED_PROTESTER_SCHEMA: dict[str, FieldSchema] = {
942
973
  }
943
974
 
944
975
  REVISIONS_SCHEMA: dict[str, FieldSchema] = {
945
- "changed_fields": FieldSchema(name="changed_fields", type=str, is_optional=True, is_list=False),
946
- "changes": FieldSchema(name="changes", type=str, is_optional=True, is_list=False),
976
+ "changed_fields": FieldSchema(name="changed_fields", type=str, is_optional=True, is_list=True),
977
+ "changes": FieldSchema(name="changes", type=dict, is_optional=True, is_list=False),
947
978
  "kind": FieldSchema(name="kind", type=str, is_optional=True, is_list=False),
948
979
  "observed_at": FieldSchema(name="observed_at", type=datetime, is_optional=True, is_list=False),
949
- "sequence": FieldSchema(name="sequence", type=str, is_optional=True, is_list=False),
980
+ "sequence": FieldSchema(name="sequence", type=int, is_optional=True, is_list=False),
950
981
  "source_declared": FieldSchema(
951
- name="source_declared", type=str, is_optional=True, is_list=False
982
+ name="source_declared", type=bool, is_optional=True, is_list=False
952
983
  ),
953
984
  }
954
985
 
@@ -1087,6 +1118,8 @@ GENERATED_NESTED: dict[str, dict[str, FieldSchema]] = {
1087
1118
  "Attachments2": ATTACHMENTS2_SCHEMA,
1088
1119
  "Attachments3": ATTACHMENTS3_SCHEMA,
1089
1120
  "Attachments4": ATTACHMENTS4_SCHEMA,
1121
+ "Attachments5": ATTACHMENTS5_SCHEMA,
1122
+ "Attachments6": ATTACHMENTS6_SCHEMA,
1090
1123
  "Awardee": AWARDEE_SCHEMA,
1091
1124
  "AwardingOffice": AWARDING_OFFICE_SCHEMA,
1092
1125
  "AwardingOffice2": AWARDING_OFFICE2_SCHEMA,
@@ -2185,7 +2218,7 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2185
2218
  type=dict,
2186
2219
  is_optional=True,
2187
2220
  is_list=True,
2188
- nested_model="Attachments",
2221
+ nested_model="Attachments2",
2189
2222
  ),
2190
2223
  "meta": FieldSchema(
2191
2224
  name="meta", type=dict, is_optional=True, is_list=False, nested_model="Meta"
@@ -2433,7 +2466,7 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2433
2466
  type=dict,
2434
2467
  is_optional=True,
2435
2468
  is_list=False,
2436
- nested_model="Attachments4",
2469
+ nested_model="Attachments6",
2437
2470
  ),
2438
2471
  "department": FieldSchema(
2439
2472
  name="department",
@@ -2751,7 +2784,7 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2751
2784
  ),
2752
2785
  "description": FieldSchema(name="description", type=str, is_optional=True, is_list=False),
2753
2786
  "estimated_advertisement_date": FieldSchema(
2754
- name="estimated_advertisement_date", type=date, is_optional=True, is_list=False
2787
+ name="estimated_advertisement_date", type=datetime, is_optional=True, is_list=False
2755
2788
  ),
2756
2789
  "estimated_advertisement_raw": FieldSchema(
2757
2790
  name="estimated_advertisement_raw", type=str, is_optional=True, is_list=False
@@ -2805,17 +2838,17 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2805
2838
  name="attachments",
2806
2839
  type=dict,
2807
2840
  is_optional=True,
2808
- is_list=False,
2809
- nested_model="Attachments3",
2841
+ is_list=True,
2842
+ nested_model="Attachments4",
2810
2843
  ),
2811
2844
  "bid_opening_date": FieldSchema(
2812
- name="bid_opening_date", type=date, is_optional=True, is_list=False
2845
+ name="bid_opening_date", type=datetime, is_optional=True, is_list=False
2813
2846
  ),
2814
2847
  "bid_opening_raw": FieldSchema(
2815
2848
  name="bid_opening_raw", type=str, is_optional=True, is_list=False
2816
2849
  ),
2817
2850
  "category_codes": FieldSchema(
2818
- name="category_codes", type=str, is_optional=True, is_list=False
2851
+ name="category_codes", type=dict, is_optional=True, is_list=True
2819
2852
  ),
2820
2853
  "contact": FieldSchema(
2821
2854
  name="contact",
@@ -2824,6 +2857,9 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2824
2857
  is_list=False,
2825
2858
  nested_model="GrantorContact",
2826
2859
  ),
2860
+ "delisted_at": FieldSchema(
2861
+ name="delisted_at", type=datetime, is_optional=True, is_list=False
2862
+ ),
2827
2863
  "description": FieldSchema(name="description", type=str, is_optional=True, is_list=False),
2828
2864
  "first_seen_at": FieldSchema(
2829
2865
  name="first_seen_at", type=datetime, is_optional=True, is_list=False
@@ -2852,16 +2888,18 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2852
2888
  is_list=False,
2853
2889
  nested_model="Organization3",
2854
2890
  ),
2855
- "posted_date": FieldSchema(name="posted_date", type=date, is_optional=True, is_list=False),
2891
+ "posted_date": FieldSchema(
2892
+ name="posted_date", type=datetime, is_optional=True, is_list=False
2893
+ ),
2856
2894
  "raw": FieldSchema(name="raw", type=dict, is_optional=True, is_list=False),
2857
2895
  "response_deadline": FieldSchema(
2858
- name="response_deadline", type=str, is_optional=True, is_list=False
2896
+ name="response_deadline", type=datetime, is_optional=True, is_list=False
2859
2897
  ),
2860
2898
  "response_deadline_original": FieldSchema(
2861
- name="response_deadline_original", type=str, is_optional=True, is_list=False
2899
+ name="response_deadline_original", type=datetime, is_optional=True, is_list=False
2862
2900
  ),
2863
2901
  "revisions": FieldSchema(
2864
- name="revisions", type=dict, is_optional=True, is_list=False, nested_model="Revisions"
2902
+ name="revisions", type=dict, is_optional=True, is_list=True, nested_model="Revisions"
2865
2903
  ),
2866
2904
  "snippet": FieldSchema(name="snippet", type=str, is_optional=True, is_list=False),
2867
2905
  "solicitation_number": FieldSchema(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tango-python
3
- Version: 1.6.0
3
+ Version: 1.7.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=JI_JOEupa1pCGXx9RLZzXNwbnXRwg3GtZXSWjkOq8tQ,2258
2
- tango/client.py,sha256=O_bjmRJbKiyz-WLKOnZYuZC585tB0ni86dKkR_2x4Xw,214117
1
+ tango/__init__.py,sha256=P7q3qdG_74A9OKteDHRNyH1pD_VuPOoBFGSFZECbVA0,2300
2
+ tango/client.py,sha256=R69lCC6XTo42lVbuWXqEJNyO0-tro0IthgwyWiuJs6Y,220172
3
3
  tango/exceptions.py,sha256=B1t27BBxHcPglAfJQyan5khNEYNAyF6Wkp2EmPMdRxM,3858
4
- tango/models.py,sha256=oljb3sihDb2ZQ5bxfjxK5x0p6vrrc032hU_C_15LGFc,53831
4
+ tango/models.py,sha256=X5ijWFnz05kgobqoPdtxRxWWW_hVfFZRtCGksbgQW-o,58377
5
5
  tango/shapes/__init__.py,sha256=7ea1WU74jp4znhNw-gXruag6m6eyPZtbVgbDFmFUWro,1072
6
- tango/shapes/explicit_schemas.py,sha256=8HgxKpAi2U80JP_MnABnW1JpYJA05aoOZSJsJIcyIWQ,71421
6
+ tango/shapes/explicit_schemas.py,sha256=WvkKaEkW01HgO56In-8vNlxZeoEkJJEYjZOUyrGItU4,73775
7
7
  tango/shapes/factory.py,sha256=uDPhnp6VVzrLBKodxteZy5AcU43juEv-qKVRWth51j0,31550
8
- tango/shapes/generated_overlay.py,sha256=s1LvxIDmkF1CfWIgY_Bb1uvswh75fo8Ui0Cg55HdGvQ,135623
8
+ tango/shapes/generated_overlay.py,sha256=wv3Z4MjsU7xyk8EKzUNG1BgfAxgukGcsQ_5QkPPJyvA,137957
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.6.0.dist-info/METADATA,sha256=xyFCdsg7NYnM78fKWbD0TS9J84pg9QCIHo_KJXMDqaA,21779
20
- tango_python-1.6.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
21
- tango_python-1.6.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
22
- tango_python-1.6.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
23
- tango_python-1.6.0.dist-info/RECORD,,
19
+ tango_python-1.7.0.dist-info/METADATA,sha256=1Jf7m5SbI4g54SfWLy1LQxFVz-W2LA1uLbYA3MBkBlE,21779
20
+ tango_python-1.7.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
21
+ tango_python-1.7.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
22
+ tango_python-1.7.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
23
+ tango_python-1.7.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.0
2
+ Generator: hatchling 1.32.4
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any