tango-python 1.6.0__py3-none-any.whl → 1.8.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,10 +10,12 @@ from .exceptions import (
10
10
  )
11
11
  from .models import (
12
12
  BudgetAccount,
13
+ ContractAppeal,
13
14
  DibbsAward,
14
15
  DibbsRfp,
15
16
  DibbsRfq,
16
17
  Exclusion,
18
+ FederalRegisterDocument,
17
19
  GsaElibraryContract,
18
20
  ITDashboardInvestment,
19
21
  PaginatedResponse,
@@ -53,7 +55,7 @@ from .webhooks import (
53
55
  )
54
56
  from .webhooks.receiver import Delivery, WebhookReceiver
55
57
 
56
- __version__ = "1.6.0"
58
+ __version__ = "1.8.0"
57
59
  __all__ = [
58
60
  "TangoClient",
59
61
  "TangoAPIError",
@@ -65,6 +67,8 @@ __all__ = [
65
67
  "ResolveCandidate",
66
68
  "ResolveResult",
67
69
  "BudgetAccount",
70
+ "ContractAppeal",
71
+ "FederalRegisterDocument",
68
72
  "DibbsAward",
69
73
  "DibbsRfp",
70
74
  "DibbsRfq",
tango/client.py CHANGED
@@ -24,11 +24,13 @@ from tango.models import (
24
24
  BudgetAccount,
25
25
  BusinessType,
26
26
  Contract,
27
+ ContractAppeal,
27
28
  DibbsAward,
28
29
  DibbsRfp,
29
30
  DibbsRfq,
30
31
  Entity,
31
32
  Exclusion,
33
+ FederalRegisterDocument,
32
34
  Forecast,
33
35
  Grant,
34
36
  GsaElibraryContract,
@@ -1600,18 +1602,18 @@ class TangoClient:
1600
1602
  cio_rating_max: int | None = None,
1601
1603
  performance_risk: bool | None = None,
1602
1604
  previous_uii: str | None = None,
1605
+ agency: str | None = None,
1603
1606
  ) -> PaginatedResponse:
1604
1607
  """List federal IT investments from the IT Dashboard (`/api/itdashboard/`).
1605
1608
 
1606
1609
  Filters are tier-gated by the API:
1607
1610
 
1608
- - **Free**: ``search`` (full-text across UII, title, description, agency,
1609
- bureau) and ``previous_uii`` — following a retired identifier forward to
1610
- whatever superseded it is recovery, not analysis, so it is ungated
1611
+ - **Free**: ``search`` (full-text across UII, title, description, agency, bureau), ``agency_name`` (text), ``agency`` and ``previous_uii`` — following a retired identifier forward to whatever superseded it is recovery, not analysis, so it is ungated
1611
1612
  - **Pro**: ``agency_code``, ``type_of_investment``,
1612
1613
  ``updated_time_after`` / ``updated_time_before``
1613
- - **Business+**: ``agency_name`` (text), ``cio_rating``,
1614
- ``cio_rating_max``, ``performance_risk``
1614
+ - **Business+**: ``cio_rating``, ``cio_rating_max``, ``performance_risk``
1615
+
1616
+ ``agency`` takes a Tango agency name, abbreviation, code or organization key, e.g. ``DOT``, and matches the whole organization subtree, so a department includes its sub-agencies. OR several with ``|``.
1615
1617
 
1616
1618
  Hitting a gated filter on a lower tier returns a 403 with upgrade info.
1617
1619
 
@@ -1631,6 +1633,7 @@ class TangoClient:
1631
1633
  params["flat_lists"] = "true"
1632
1634
  for k, val in (
1633
1635
  ("search", search),
1636
+ ("agency", agency),
1634
1637
  ("agency_code", agency_code),
1635
1638
  ("agency_name", agency_name),
1636
1639
  ("type_of_investment", type_of_investment),
@@ -2592,18 +2595,18 @@ class TangoClient:
2592
2595
  shape: Response shape string (defaults to minimal shape)
2593
2596
  flat: If True, flatten nested objects in shaped response
2594
2597
  flat_lists: If True, flatten arrays using indexed keys
2595
- source_system: Filter by source system (e.g. gao)
2598
+ source_system: Filter by source system: gao, cofc, or sba_oha
2596
2599
  outcome: Filter by outcome (e.g. Denied, Dismissed, Withdrawn, Sustained)
2597
2600
  case_type: Filter by case type
2598
2601
  agency: Filter by protested agency text
2599
- case_number: Filter by case number (e.g. b-423274)
2602
+ 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
2603
  solicitation_number: Filter by exact solicitation number
2601
2604
  protester: Filter by protester name text
2602
2605
  filed_date_after: Filed date on or after
2603
2606
  filed_date_before: Filed date on or before
2604
2607
  decision_date_after: Decision date on or after
2605
2608
  decision_date_before: Decision date on or before
2606
- naics_code: Filter by the solicitation's NAICS code (e.g. 541519)
2609
+ 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
2610
  search: Full-text search over protest searchable fields
2608
2611
  """
2609
2612
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
@@ -2682,6 +2685,284 @@ class TangoClient:
2682
2685
  data = self._get(f"/api/protests/{case_id}/", params)
2683
2686
  return self._parse_response_with_shape(data, shape, Protest, flat, flat_lists)
2684
2687
 
2688
+ # ============================================================================
2689
+ # Contract Disputes Act appeals (CBCA and ASBCA)
2690
+ # ============================================================================
2691
+
2692
+ def list_contract_appeals(
2693
+ self,
2694
+ page: int = 1,
2695
+ limit: int = 25,
2696
+ shape: str | None = None,
2697
+ flat: bool = False,
2698
+ flat_lists: bool = False,
2699
+ board: str | None = None,
2700
+ docket: str | None = None,
2701
+ appellant: str | None = None,
2702
+ judge: str | None = None,
2703
+ decision_type: str | None = None,
2704
+ decision_date_after: str | None = None,
2705
+ decision_date_before: str | None = None,
2706
+ listed: bool | None = None,
2707
+ document_id: str | None = None,
2708
+ search: str | None = None,
2709
+ ordering: str | None = None,
2710
+ ) -> PaginatedResponse:
2711
+ """
2712
+ List Contract Disputes Act appeal decisions.
2713
+
2714
+ Decisions of the Civilian Board of Contract Appeals (CBCA) and the Armed
2715
+ Services Board of Contract Appeals (ASBCA) — appeals of a contracting
2716
+ officer's decision, which is a different dispute from a bid protest. For
2717
+ protests, use :meth:`list_protests`.
2718
+
2719
+ Args:
2720
+ page: Page number
2721
+ limit: Results per page (max 100)
2722
+ shape: Response shape string (defaults to minimal shape)
2723
+ flat: If True, flatten nested objects in shaped response
2724
+ flat_lists: If True, flatten arrays using indexed keys
2725
+ board: ``cbca`` or ``asbca``. Multi-value: ``cbca|asbca``
2726
+ docket: Exact docket match, e.g. ``3288-R``, ``59116`` or
2727
+ ``7092-C(6682, 6765, 6767)``. A leading ``CBCA`` / ``ASBCA`` and
2728
+ ``No.`` / ``Nos.`` are ignored, so a docket can be pasted as it is
2729
+ cited. Commas are part of a docket, never a separator; OR several
2730
+ with ``|``
2731
+ appellant: Case-insensitive substring match on the appellant's name
2732
+ (min 2 characters)
2733
+ judge: Case-insensitive exact match on the judge as the listing names
2734
+ them
2735
+ decision_type: CBCA only — ``Decision``, ``Dismissal``, ``Order``,
2736
+ ``Full Board Order``, or the listing's own text where it matches
2737
+ none of those. ASBCA rows carry no type
2738
+ decision_date_after: Decided on or after (YYYY-MM-DD)
2739
+ decision_date_before: Decided on or before (YYYY-MM-DD)
2740
+ listed: Whether the board's newest listing still carries the decision.
2741
+ ``False`` returns the ones it has dropped, which are kept here
2742
+ rather than deleted
2743
+ document_id: The board's own document id
2744
+ search: Ranked full-text search over the appellant and the full
2745
+ decision text (min 2 characters). Wrap in double quotes for a
2746
+ phrase. Free at every plan, and it returns no fragment of the
2747
+ text — only reading ``decision_text`` needs Enterprise
2748
+ ordering: Sort field — ``decision_date`` (the default, as
2749
+ ``-decision_date``), ``appellant``, ``first_listed_at`` or
2750
+ ``rank``. ``rank`` requires a non-empty ``search``
2751
+ """
2752
+ params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
2753
+
2754
+ if shape is None:
2755
+ shape = ShapeConfig.CONTRACT_APPEALS_MINIMAL
2756
+ if shape:
2757
+ params["shape"] = shape
2758
+ if flat:
2759
+ params["flat"] = "true"
2760
+ if flat_lists:
2761
+ params["flat_lists"] = "true"
2762
+
2763
+ for key, val in (
2764
+ ("board", board),
2765
+ ("docket", docket),
2766
+ ("appellant", appellant),
2767
+ ("judge", judge),
2768
+ ("decision_type", decision_type),
2769
+ ("decision_date_after", decision_date_after),
2770
+ ("decision_date_before", decision_date_before),
2771
+ ("listed", listed),
2772
+ ("document_id", document_id),
2773
+ ("search", search),
2774
+ ("ordering", ordering),
2775
+ ):
2776
+ if val is not None:
2777
+ params[key] = val
2778
+
2779
+ data = self._get("/api/contract_appeals/", params)
2780
+
2781
+ results = [
2782
+ self._parse_response_with_shape(item, shape, ContractAppeal, flat, flat_lists)
2783
+ for item in data["results"]
2784
+ ]
2785
+
2786
+ return PaginatedResponse(
2787
+ count=data["count"],
2788
+ next=data.get("next"),
2789
+ previous=data.get("previous"),
2790
+ results=results,
2791
+ meta=data.get("meta"),
2792
+ )
2793
+
2794
+ def get_contract_appeal(
2795
+ self,
2796
+ appeal_id: str,
2797
+ shape: str | None = None,
2798
+ flat: bool = False,
2799
+ flat_lists: bool = False,
2800
+ ) -> Any:
2801
+ """
2802
+ Get a single Contract Disputes Act appeal decision by uuid.
2803
+
2804
+ Args:
2805
+ appeal_id: Decision UUID
2806
+ shape: Response shape string (defaults to the comprehensive shape)
2807
+ flat: If True, flatten nested objects in shaped response
2808
+ flat_lists: If True, flatten arrays using indexed keys
2809
+ """
2810
+ params: dict[str, Any] = {}
2811
+ if shape is None:
2812
+ shape = ShapeConfig.CONTRACT_APPEALS_COMPREHENSIVE
2813
+ if shape:
2814
+ params["shape"] = shape
2815
+ if flat:
2816
+ params["flat"] = "true"
2817
+ if flat_lists:
2818
+ params["flat_lists"] = "true"
2819
+
2820
+ data = self._get(f"/api/contract_appeals/{appeal_id}/", params)
2821
+ return self._parse_response_with_shape(data, shape, ContractAppeal, flat, flat_lists)
2822
+
2823
+ # ============================================================================
2824
+ # Federal Register documents
2825
+ # ============================================================================
2826
+
2827
+ def list_federal_register_documents(
2828
+ self,
2829
+ page: int = 1,
2830
+ limit: int = 25,
2831
+ shape: str | None = None,
2832
+ flat: bool = False,
2833
+ flat_lists: bool = False,
2834
+ search: str | None = None,
2835
+ document_number: str | None = None,
2836
+ type: str | None = None,
2837
+ agency: str | None = None,
2838
+ fr_agency: str | None = None,
2839
+ publication_date_after: str | None = None,
2840
+ publication_date_before: str | None = None,
2841
+ effective_on_after: str | None = None,
2842
+ effective_on_before: str | None = None,
2843
+ comments_close_on_after: str | None = None,
2844
+ comments_close_on_before: str | None = None,
2845
+ comments_open: bool | None = None,
2846
+ cfr_title: str | None = None,
2847
+ cfr_part: str | None = None,
2848
+ significant: bool | None = None,
2849
+ rin: str | None = None,
2850
+ executive_order_number: str | None = None,
2851
+ ordering: str | None = None,
2852
+ ) -> PaginatedResponse:
2853
+ """
2854
+ List Federal Register documents.
2855
+
2856
+ Rules, proposed rules, notices and presidential documents published in the Federal Register since 1994.
2857
+
2858
+ Args:
2859
+ page: Page number
2860
+ limit: Results per page (max 100)
2861
+ shape: Response shape string (defaults to minimal shape)
2862
+ flat: If True, flatten nested objects in shaped response
2863
+ flat_lists: If True, flatten arrays using indexed keys
2864
+ search: Ranked full-text search over the title, abstract and action. Wrap in double quotes for a phrase
2865
+ document_number: Exact FR document number, e.g. ``2016-31922``. OR several with ``|``. A number the Federal Register reused before 2016 matches every document that carries it
2866
+ type: ``Notice``, ``Rule``, ``Proposed Rule``, ``Presidential Document``, ``Correction``, ``Sunshine Act Document`` or ``Uncategorized Document`` (case-insensitive). OR several with ``|``. Documents published before 2008 are mostly ``Uncategorized Document``
2867
+ agency: A Tango agency name, abbreviation, code or organization key, e.g. ``EPA``. Matches a document when any agency it lists falls within that organization, so a department includes its sub-agencies. OR several with ``|``
2868
+ fr_agency: The Federal Register's own agency slug, e.g. ``environmental-protection-agency``. OR several with ``|``
2869
+ publication_date_after: Published on or after (YYYY-MM-DD)
2870
+ publication_date_before: Published on or before (YYYY-MM-DD)
2871
+ effective_on_after: Effective on or after (YYYY-MM-DD)
2872
+ effective_on_before: Effective on or before (YYYY-MM-DD)
2873
+ comments_close_on_after: Comment period closes on or after (YYYY-MM-DD)
2874
+ comments_close_on_before: Comment period closes on or before (YYYY-MM-DD)
2875
+ comments_open: ``True`` for documents whose comment period closes today or later, ``False`` for those already closed. Documents with no comment deadline match neither
2876
+ cfr_title: A CFR title number, e.g. ``40``
2877
+ cfr_part: A CFR part number, e.g. ``52``. Requires ``cfr_title``, and both must match the same CFR reference
2878
+ significant: Significant under Executive Order 12866
2879
+ rin: A Regulation Identifier Number, e.g. ``2060-AV16``. OR several with ``|``
2880
+ executive_order_number: Exact executive order number
2881
+ ordering: Sort field — ``publication_date`` (the default, as ``-publication_date``), ``effective_on``, ``comments_close_on``, ``document_number`` or ``rank``. ``rank`` requires a non-empty ``search``
2882
+ """
2883
+ params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
2884
+
2885
+ if shape is None:
2886
+ shape = ShapeConfig.FEDERAL_REGISTER_MINIMAL
2887
+ if shape:
2888
+ params["shape"] = shape
2889
+ if flat:
2890
+ params["flat"] = "true"
2891
+ if flat_lists:
2892
+ params["flat_lists"] = "true"
2893
+
2894
+ for key, val in (
2895
+ ("search", search),
2896
+ ("document_number", document_number),
2897
+ ("type", type),
2898
+ ("agency", agency),
2899
+ ("fr_agency", fr_agency),
2900
+ ("publication_date_after", publication_date_after),
2901
+ ("publication_date_before", publication_date_before),
2902
+ ("effective_on_after", effective_on_after),
2903
+ ("effective_on_before", effective_on_before),
2904
+ ("comments_close_on_after", comments_close_on_after),
2905
+ ("comments_close_on_before", comments_close_on_before),
2906
+ ("comments_open", comments_open),
2907
+ ("cfr_title", cfr_title),
2908
+ ("cfr_part", cfr_part),
2909
+ ("significant", significant),
2910
+ ("rin", rin),
2911
+ ("executive_order_number", executive_order_number),
2912
+ ("ordering", ordering),
2913
+ ):
2914
+ if val is not None:
2915
+ params[key] = val
2916
+
2917
+ data = self._get("/api/federal_register/", params)
2918
+
2919
+ results = [
2920
+ self._parse_response_with_shape(item, shape, FederalRegisterDocument, flat, flat_lists)
2921
+ for item in data["results"]
2922
+ ]
2923
+
2924
+ return PaginatedResponse(
2925
+ count=data["count"],
2926
+ next=data.get("next"),
2927
+ previous=data.get("previous"),
2928
+ results=results,
2929
+ meta=data.get("meta"),
2930
+ )
2931
+
2932
+ def get_federal_register_document(
2933
+ self,
2934
+ document_uuid: str,
2935
+ shape: str | None = None,
2936
+ flat: bool = False,
2937
+ flat_lists: bool = False,
2938
+ ) -> Any:
2939
+ """
2940
+ Get a single Federal Register document by uuid.
2941
+
2942
+ The detail route takes the document's ``uuid``, not its ``document_number``, which is not unique on its own.
2943
+ To look a document up by number, use ``list_federal_register_documents(document_number=...)``.
2944
+
2945
+ Args:
2946
+ document_uuid: Document UUID
2947
+ shape: Response shape string (defaults to the comprehensive shape). Name ``full_text`` here to get the document's plain text, which can run to several MB
2948
+ flat: If True, flatten nested objects in shaped response
2949
+ flat_lists: If True, flatten arrays using indexed keys
2950
+ """
2951
+ params: dict[str, Any] = {}
2952
+ if shape is None:
2953
+ shape = ShapeConfig.FEDERAL_REGISTER_COMPREHENSIVE
2954
+ if shape:
2955
+ params["shape"] = shape
2956
+ if flat:
2957
+ params["flat"] = "true"
2958
+ if flat_lists:
2959
+ params["flat_lists"] = "true"
2960
+
2961
+ data = self._get(f"/api/federal_register/{document_uuid}/", params)
2962
+ return self._parse_response_with_shape(
2963
+ data, shape, FederalRegisterDocument, flat, flat_lists
2964
+ )
2965
+
2685
2966
  # ============================================================================
2686
2967
  # DLA DIBBS (RFQs, RFPs, awards)
2687
2968
  # ============================================================================
@@ -3106,6 +3387,7 @@ class TangoClient:
3106
3387
  update_date_before: str | None = None,
3107
3388
  search: str | None = None,
3108
3389
  ordering: str | None = None,
3390
+ agency: str | None = None,
3109
3391
  ) -> PaginatedResponse:
3110
3392
  """
3111
3393
  List SAM.gov exclusion (debarment) records.
@@ -3125,6 +3407,7 @@ class TangoClient:
3125
3407
  classification_type: Filter by classification (Firm, Individual, Vessel, ...)
3126
3408
  exclusion_type: Filter by exclusion type
3127
3409
  exclusion_program: Filter by exclusion program
3410
+ agency: Excluding agency as a Tango organization. A Tango agency name, abbreviation, code or organization key, e.g. ``EPA``. Matches the whole organization subtree, so a department includes its sub-agencies. OR several with ``|``
3128
3411
  excluding_agency_code: Filter by excluding agency code
3129
3412
  excluding_agency_name: Filter by excluding agency name
3130
3413
  active: True returns only records currently in effect.
@@ -3160,6 +3443,7 @@ class TangoClient:
3160
3443
  ("classification_type", classification_type),
3161
3444
  ("exclusion_type", exclusion_type),
3162
3445
  ("exclusion_program", exclusion_program),
3446
+ ("agency", agency),
3163
3447
  ("excluding_agency_code", excluding_agency_code),
3164
3448
  ("excluding_agency_name", excluding_agency_name),
3165
3449
  ("active", active),
@@ -4017,6 +4301,7 @@ class TangoClient:
4017
4301
  actual_vs_requested_contract_capped_lte: float | None = None,
4018
4302
  search: str | None = None,
4019
4303
  ordering: str | None = None,
4304
+ agency: str | None = None,
4020
4305
  ) -> PaginatedResponse:
4021
4306
  """List budget accounts (`/api/budget/accounts/`).
4022
4307
 
@@ -4034,6 +4319,7 @@ class TangoClient:
4034
4319
  fiscal_year: Fiscal year (exact).
4035
4320
  fiscal_year_gte: Fiscal year >=.
4036
4321
  fiscal_year_lte: Fiscal year <=.
4322
+ agency: A Tango agency name, abbreviation, code or organization key, e.g. ``EPA``. Matches the whole organization subtree, so a department includes its sub-agencies. OR several with ``|``.
4037
4323
  agency_code: Agency code (exact).
4038
4324
  bureau_name: Bureau name (exact).
4039
4325
  account_title: Account title (icontains).
@@ -4098,6 +4384,7 @@ class TangoClient:
4098
4384
  ("fiscal_year", fiscal_year),
4099
4385
  ("fiscal_year__gte", fiscal_year_gte),
4100
4386
  ("fiscal_year__lte", fiscal_year_lte),
4387
+ ("agency", agency),
4101
4388
  ("agency_code", agency_code),
4102
4389
  ("bureau_name", bureau_name),
4103
4390
  ("account_title__icontains", account_title),
@@ -4733,7 +5020,10 @@ class TangoClient:
4733
5020
  Args:
4734
5021
  name: Human-readable name for this alert.
4735
5022
  query_type: One of ``opportunity``, ``contract``, ``idv``, ``ota``,
4736
- ``otidv``, ``entity``, ``grant``, ``forecast``.
5023
+ ``otidv``, ``entity``, ``grant``, ``forecast``, ``exclusion``,
5024
+ ``dibbs_rfq``, ``dibbs_rfp``, ``sled_opportunity``,
5025
+ ``contract_appeal`` or ``federal_register``. :meth:`list_webhook_event_types` is the
5026
+ current authority — this list can only go stale.
4737
5027
  filters: Dict of query parameters that the alert matches against
4738
5028
  (e.g. ``{"naics": "541330", "set_aside": "SBA"}``).
4739
5029
  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,110 @@ 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
+
1216
+ class FederalRegisterDocument:
1217
+ """Schema definition for FederalRegisterDocument (not used for instances).
1218
+
1219
+ Federal Register documents at ``/api/federal_register/``: rules, proposed rules, notices and presidential documents published since 1994.
1220
+ Shape-on-demand, so every field is optional.
1221
+
1222
+ A document is identified by ``uuid``.
1223
+ ``document_number`` alone is not unique: the Federal Register reused some numbers before 2016, so ``document_number=`` can match more than one document.
1224
+
1225
+ ``agencies``, ``cfr_references``, ``dockets`` and ``topics`` are the Federal Register's own structures, served as published.
1226
+ ``full_text`` can run to several MB, so it is available on the detail endpoint only and only when named in ``shape``.
1227
+ """
1228
+
1229
+ uuid: str | None = None
1230
+ document_number: str | None = None
1231
+ publication_date: date | None = None
1232
+ citation: str | None = None
1233
+ title: str | None = None
1234
+ type: str | None = None
1235
+ subtype: str | None = None
1236
+ abstract: str | None = None
1237
+ action: str | None = None
1238
+ dates: str | None = None
1239
+ comments_close_on: date | None = None
1240
+ effective_on: date | None = None
1241
+ signing_date: date | None = None
1242
+ start_page: int | None = None
1243
+ end_page: int | None = None
1244
+ page_length: int | None = None
1245
+ volume: int | None = None
1246
+ significant: bool | None = None
1247
+ toc_doc: str | None = None
1248
+ toc_subject: str | None = None
1249
+ correction_of: str | None = None
1250
+ corrections: list[str] | None = None
1251
+ disposition_notes: str | None = None
1252
+ executive_order_notes: str | None = None
1253
+ executive_order_number: str | None = None
1254
+ presidential_document_number: str | None = None
1255
+ proclamation_number: str | None = None
1256
+ not_received_for_publication: str | None = None
1257
+ comment_url: str | None = None
1258
+ regulations_dot_gov_url: str | None = None
1259
+ html_url: str | None = None
1260
+ body_html_url: str | None = None
1261
+ pdf_url: str | None = None
1262
+ public_inspection_pdf_url: str | None = None
1263
+ raw_text_url: str | None = None
1264
+ agencies: list[dict[str, Any]] | None = None
1265
+ cfr_references: list[dict[str, Any]] | None = None
1266
+ dockets: list[dict[str, Any]] | None = None
1267
+ docket_ids: list[str] | None = None
1268
+ topics: list[str] | None = None
1269
+ regulation_id_numbers: list[str] | None = None
1270
+ regulation_id_number_info: dict[str, Any] | None = None
1271
+ regulations_dot_gov_info: dict[str, Any] | None = None
1272
+ full_text: str | None = None
1273
+
1274
+
1154
1275
  @dataclass
1155
1276
  class PaginatedResponse[T]:
1156
1277
  """Paginated API response
@@ -1317,6 +1438,39 @@ class ShapeConfig:
1317
1438
  # Default for list_protests()
1318
1439
  PROTESTS_MINIMAL: Final = "case_id,case_number,title,source_system,outcome,filed_date"
1319
1440
 
1441
+ # Default for list_contract_appeals(). Mirrors the API's own list default.
1442
+ CONTRACT_APPEALS_MINIMAL: Final = (
1443
+ "uuid,board,docket_numbers,decision_date,appellant,judge,decision_type,url"
1444
+ )
1445
+
1446
+ # Default for get_contract_appeal(). Mirrors the API's own retrieve default.
1447
+ # `decision_text` is left out on purpose: it runs to ~100K characters and
1448
+ # needs an Enterprise plan, so naming it by default would make every detail
1449
+ # fetch ask for a body most callers cannot read.
1450
+ CONTRACT_APPEALS_COMPREHENSIVE: Final = (
1451
+ "uuid,board,docket_numbers,decision_date,appellant,judge,decision_type,url,"
1452
+ "docket_raw,docket_source,decision_date_repaired,decision_type_raw,"
1453
+ "listing_year,first_listed_at,listed,text_status,text_char_count"
1454
+ )
1455
+
1456
+ # Default for list_federal_register_documents(). Mirrors the API's own list default.
1457
+ FEDERAL_REGISTER_MINIMAL: Final = (
1458
+ "uuid,document_number,publication_date,type,subtype,title,abstract,action,"
1459
+ "agencies,cfr_references,citation,significant,comments_close_on,effective_on,"
1460
+ "html_url,pdf_url"
1461
+ )
1462
+
1463
+ # Default for get_federal_register_document(). Mirrors the API's own retrieve default.
1464
+ # `full_text` is left out on purpose: it can run to several MB.
1465
+ FEDERAL_REGISTER_COMPREHENSIVE: Final = (
1466
+ "uuid,document_number,publication_date,type,subtype,title,abstract,action,"
1467
+ "agencies,cfr_references,citation,significant,comments_close_on,effective_on,"
1468
+ "html_url,pdf_url,dates,signing_date,start_page,end_page,volume,docket_ids,"
1469
+ "dockets,regulation_id_numbers,topics,correction_of,corrections,"
1470
+ "executive_order_number,presidential_document_number,proclamation_number,"
1471
+ "comment_url,regulations_dot_gov_url,raw_text_url,body_html_url"
1472
+ )
1473
+
1320
1474
  # Default for list_dibbs_rfqs()
1321
1475
  DIBBS_RFQS_MINIMAL: Final = (
1322
1476
  "uuid,solicitation,nsn,part_number,nomenclature,quantity,issue_date,return_by_date,is_open"
@@ -1356,8 +1510,9 @@ class ShapeConfig:
1356
1510
  # API (median ~550 chars, tail past 120k), so it is not in the list default.
1357
1511
  SLED_OPPORTUNITIES_MINIMAL: Final = (
1358
1512
  "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"
1513
+ "jurisdiction,agency,status,status_reason,delisted_at,posted_date,"
1514
+ "response_deadline,source_url,has_documents,first_seen_at,"
1515
+ "last_change_seen_at"
1361
1516
  )
1362
1517
 
1363
1518
  # Default for get_sled_opportunity(). `attachments(*)` deliberately does not
@@ -1367,8 +1522,8 @@ class ShapeConfig:
1367
1522
  SLED_OPPORTUNITIES_COMPREHENSIVE: Final = (
1368
1523
  "opportunity_id,solicitation_number,solicitation_type,"
1369
1524
  "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,"
1525
+ "status,status_reason,delisted_at,status_computed_at,source_status,"
1526
+ "source_url,posted_date,response_deadline,response_deadline_original,"
1372
1527
  "bid_opening_date,bid_opening_raw,category_codes,has_documents,"
1373
1528
  "first_seen_at,last_seen_at,last_change_seen_at,"
1374
1529
  "organization(*),contact(*),meta(*),attachments(*),revisions(*)"
@@ -763,6 +763,120 @@ 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
+
803
+ FEDERAL_REGISTER_DOCUMENT_SCHEMA: dict[str, FieldSchema] = {
804
+ "uuid": FieldSchema(name="uuid", type=str, is_optional=True, is_list=False),
805
+ "document_number": FieldSchema(
806
+ name="document_number", type=str, is_optional=True, is_list=False
807
+ ),
808
+ "publication_date": FieldSchema(
809
+ name="publication_date", type=date, is_optional=True, is_list=False
810
+ ),
811
+ "citation": FieldSchema(name="citation", type=str, is_optional=True, is_list=False),
812
+ "title": FieldSchema(name="title", type=str, is_optional=True, is_list=False),
813
+ "type": FieldSchema(name="type", type=str, is_optional=True, is_list=False),
814
+ "subtype": FieldSchema(name="subtype", type=str, is_optional=True, is_list=False),
815
+ "abstract": FieldSchema(name="abstract", type=str, is_optional=True, is_list=False),
816
+ "action": FieldSchema(name="action", type=str, is_optional=True, is_list=False),
817
+ "dates": FieldSchema(name="dates", type=str, is_optional=True, is_list=False),
818
+ "comments_close_on": FieldSchema(
819
+ name="comments_close_on", type=date, is_optional=True, is_list=False
820
+ ),
821
+ "effective_on": FieldSchema(name="effective_on", type=date, is_optional=True, is_list=False),
822
+ "signing_date": FieldSchema(name="signing_date", type=date, is_optional=True, is_list=False),
823
+ "start_page": FieldSchema(name="start_page", type=int, is_optional=True, is_list=False),
824
+ "end_page": FieldSchema(name="end_page", type=int, is_optional=True, is_list=False),
825
+ "page_length": FieldSchema(name="page_length", type=int, is_optional=True, is_list=False),
826
+ "volume": FieldSchema(name="volume", type=int, is_optional=True, is_list=False),
827
+ "significant": FieldSchema(name="significant", type=bool, is_optional=True, is_list=False),
828
+ "toc_doc": FieldSchema(name="toc_doc", type=str, is_optional=True, is_list=False),
829
+ "toc_subject": FieldSchema(name="toc_subject", type=str, is_optional=True, is_list=False),
830
+ "correction_of": FieldSchema(name="correction_of", type=str, is_optional=True, is_list=False),
831
+ "corrections": FieldSchema(name="corrections", type=str, is_optional=True, is_list=True),
832
+ "disposition_notes": FieldSchema(
833
+ name="disposition_notes", type=str, is_optional=True, is_list=False
834
+ ),
835
+ "executive_order_notes": FieldSchema(
836
+ name="executive_order_notes", type=str, is_optional=True, is_list=False
837
+ ),
838
+ "executive_order_number": FieldSchema(
839
+ name="executive_order_number", type=str, is_optional=True, is_list=False
840
+ ),
841
+ "presidential_document_number": FieldSchema(
842
+ name="presidential_document_number", type=str, is_optional=True, is_list=False
843
+ ),
844
+ "proclamation_number": FieldSchema(
845
+ name="proclamation_number", type=str, is_optional=True, is_list=False
846
+ ),
847
+ "not_received_for_publication": FieldSchema(
848
+ name="not_received_for_publication", type=str, is_optional=True, is_list=False
849
+ ),
850
+ "comment_url": FieldSchema(name="comment_url", type=str, is_optional=True, is_list=False),
851
+ "regulations_dot_gov_url": FieldSchema(
852
+ name="regulations_dot_gov_url", type=str, is_optional=True, is_list=False
853
+ ),
854
+ "html_url": FieldSchema(name="html_url", type=str, is_optional=True, is_list=False),
855
+ "body_html_url": FieldSchema(name="body_html_url", type=str, is_optional=True, is_list=False),
856
+ "pdf_url": FieldSchema(name="pdf_url", type=str, is_optional=True, is_list=False),
857
+ "public_inspection_pdf_url": FieldSchema(
858
+ name="public_inspection_pdf_url", type=str, is_optional=True, is_list=False
859
+ ),
860
+ "raw_text_url": FieldSchema(name="raw_text_url", type=str, is_optional=True, is_list=False),
861
+ "agencies": FieldSchema(name="agencies", type=dict, is_optional=True, is_list=True),
862
+ "cfr_references": FieldSchema(name="cfr_references", type=dict, is_optional=True, is_list=True),
863
+ "dockets": FieldSchema(name="dockets", type=dict, is_optional=True, is_list=True),
864
+ "docket_ids": FieldSchema(name="docket_ids", type=str, is_optional=True, is_list=True),
865
+ "topics": FieldSchema(name="topics", type=str, is_optional=True, is_list=True),
866
+ "regulation_id_numbers": FieldSchema(
867
+ name="regulation_id_numbers", type=str, is_optional=True, is_list=True
868
+ ),
869
+ "regulation_id_number_info": FieldSchema(
870
+ name="regulation_id_number_info", type=dict, is_optional=True, is_list=False
871
+ ),
872
+ "regulations_dot_gov_info": FieldSchema(
873
+ name="regulations_dot_gov_info", type=dict, is_optional=True, is_list=False
874
+ ),
875
+ # Detail endpoint only, and only when named in the shape.
876
+ "full_text": FieldSchema(name="full_text", type=str, is_optional=True, is_list=False),
877
+ }
878
+
879
+
766
880
  AGENCY_SCHEMA: dict[str, FieldSchema] = {
767
881
  "abbreviation": FieldSchema(name="abbreviation", type=str, is_optional=True, is_list=False),
768
882
  "code": FieldSchema(name="code", type=str, is_optional=False, is_list=False),
@@ -1507,6 +1621,8 @@ EXPLICIT_SCHEMAS: dict[str, dict[str, FieldSchema]] = {
1507
1621
  "Notice": NOTICE_SCHEMA,
1508
1622
  "Protest": PROTEST_SCHEMA,
1509
1623
  "ProtestDocket": PROTEST_DOCKET_SCHEMA,
1624
+ "ContractAppeal": CONTRACT_APPEAL_SCHEMA,
1625
+ "FederalRegisterDocument": FEDERAL_REGISTER_DOCUMENT_SCHEMA,
1510
1626
  "Agency": AGENCY_SCHEMA,
1511
1627
  "Grant": GRANT_SCHEMA,
1512
1628
  # 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,
@@ -1349,6 +1382,16 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
1349
1382
  "on_off_budget": FieldSchema(
1350
1383
  name="on_off_budget", type=str, is_optional=True, is_list=False
1351
1384
  ),
1385
+ "organization": FieldSchema(
1386
+ name="organization",
1387
+ type=dict,
1388
+ is_optional=True,
1389
+ is_list=False,
1390
+ nested_model="AwardingOffice",
1391
+ ),
1392
+ "organization_id": FieldSchema(
1393
+ name="organization_id", type=str, is_optional=True, is_list=False
1394
+ ),
1352
1395
  "outlayed_to_obligated_pct": FieldSchema(
1353
1396
  name="outlayed_to_obligated_pct", type=str, is_optional=True, is_list=False
1354
1397
  ),
@@ -1878,6 +1921,16 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
1878
1921
  name="more_locations", type=str, is_optional=True, is_list=False
1879
1922
  ),
1880
1923
  "npi": FieldSchema(name="npi", type=str, is_optional=True, is_list=False),
1924
+ "organization": FieldSchema(
1925
+ name="organization",
1926
+ type=dict,
1927
+ is_optional=True,
1928
+ is_list=False,
1929
+ nested_model="AwardingOffice",
1930
+ ),
1931
+ "organization_id": FieldSchema(
1932
+ name="organization_id", type=str, is_optional=True, is_list=False
1933
+ ),
1881
1934
  "prefix": FieldSchema(name="prefix", type=str, is_optional=True, is_list=False),
1882
1935
  "primary_address": FieldSchema(
1883
1936
  name="primary_address", type=str, is_optional=True, is_list=False
@@ -2185,7 +2238,7 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2185
2238
  type=dict,
2186
2239
  is_optional=True,
2187
2240
  is_list=True,
2188
- nested_model="Attachments",
2241
+ nested_model="Attachments2",
2189
2242
  ),
2190
2243
  "meta": FieldSchema(
2191
2244
  name="meta", type=dict, is_optional=True, is_list=False, nested_model="Meta"
@@ -2433,7 +2486,7 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2433
2486
  type=dict,
2434
2487
  is_optional=True,
2435
2488
  is_list=False,
2436
- nested_model="Attachments4",
2489
+ nested_model="Attachments6",
2437
2490
  ),
2438
2491
  "department": FieldSchema(
2439
2492
  name="department",
@@ -2650,6 +2703,13 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2650
2703
  name="documents", type=dict, is_optional=True, is_list=True, nested_model="Documents"
2651
2704
  ),
2652
2705
  "end_date": FieldSchema(name="end_date", type=date, is_optional=True, is_list=False),
2706
+ "organization": FieldSchema(
2707
+ name="organization",
2708
+ type=dict,
2709
+ is_optional=True,
2710
+ is_list=False,
2711
+ nested_model="AwardingOffice",
2712
+ ),
2653
2713
  "out_of_cycle": FieldSchema(
2654
2714
  name="out_of_cycle", type=bool, is_optional=True, is_list=False
2655
2715
  ),
@@ -2703,6 +2763,13 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2703
2763
  is_list=False,
2704
2764
  nested_model="Opportunity2",
2705
2765
  ),
2766
+ "organization": FieldSchema(
2767
+ name="organization",
2768
+ type=dict,
2769
+ is_optional=True,
2770
+ is_list=False,
2771
+ nested_model="AwardingOffice",
2772
+ ),
2706
2773
  "release_date": FieldSchema(
2707
2774
  name="release_date", type=date, is_optional=True, is_list=False
2708
2775
  ),
@@ -2751,7 +2818,7 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2751
2818
  ),
2752
2819
  "description": FieldSchema(name="description", type=str, is_optional=True, is_list=False),
2753
2820
  "estimated_advertisement_date": FieldSchema(
2754
- name="estimated_advertisement_date", type=date, is_optional=True, is_list=False
2821
+ name="estimated_advertisement_date", type=datetime, is_optional=True, is_list=False
2755
2822
  ),
2756
2823
  "estimated_advertisement_raw": FieldSchema(
2757
2824
  name="estimated_advertisement_raw", type=str, is_optional=True, is_list=False
@@ -2805,17 +2872,17 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2805
2872
  name="attachments",
2806
2873
  type=dict,
2807
2874
  is_optional=True,
2808
- is_list=False,
2809
- nested_model="Attachments3",
2875
+ is_list=True,
2876
+ nested_model="Attachments4",
2810
2877
  ),
2811
2878
  "bid_opening_date": FieldSchema(
2812
- name="bid_opening_date", type=date, is_optional=True, is_list=False
2879
+ name="bid_opening_date", type=datetime, is_optional=True, is_list=False
2813
2880
  ),
2814
2881
  "bid_opening_raw": FieldSchema(
2815
2882
  name="bid_opening_raw", type=str, is_optional=True, is_list=False
2816
2883
  ),
2817
2884
  "category_codes": FieldSchema(
2818
- name="category_codes", type=str, is_optional=True, is_list=False
2885
+ name="category_codes", type=dict, is_optional=True, is_list=True
2819
2886
  ),
2820
2887
  "contact": FieldSchema(
2821
2888
  name="contact",
@@ -2824,6 +2891,9 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2824
2891
  is_list=False,
2825
2892
  nested_model="GrantorContact",
2826
2893
  ),
2894
+ "delisted_at": FieldSchema(
2895
+ name="delisted_at", type=datetime, is_optional=True, is_list=False
2896
+ ),
2827
2897
  "description": FieldSchema(name="description", type=str, is_optional=True, is_list=False),
2828
2898
  "first_seen_at": FieldSchema(
2829
2899
  name="first_seen_at", type=datetime, is_optional=True, is_list=False
@@ -2852,16 +2922,18 @@ GENERATED_OVERLAY: dict[str, dict[str, FieldSchema]] = {
2852
2922
  is_list=False,
2853
2923
  nested_model="Organization3",
2854
2924
  ),
2855
- "posted_date": FieldSchema(name="posted_date", type=date, is_optional=True, is_list=False),
2925
+ "posted_date": FieldSchema(
2926
+ name="posted_date", type=datetime, is_optional=True, is_list=False
2927
+ ),
2856
2928
  "raw": FieldSchema(name="raw", type=dict, is_optional=True, is_list=False),
2857
2929
  "response_deadline": FieldSchema(
2858
- name="response_deadline", type=str, is_optional=True, is_list=False
2930
+ name="response_deadline", type=datetime, is_optional=True, is_list=False
2859
2931
  ),
2860
2932
  "response_deadline_original": FieldSchema(
2861
- name="response_deadline_original", type=str, is_optional=True, is_list=False
2933
+ name="response_deadline_original", type=datetime, is_optional=True, is_list=False
2862
2934
  ),
2863
2935
  "revisions": FieldSchema(
2864
- name="revisions", type=dict, is_optional=True, is_list=False, nested_model="Revisions"
2936
+ name="revisions", type=dict, is_optional=True, is_list=True, nested_model="Revisions"
2865
2937
  ),
2866
2938
  "snippet": FieldSchema(name="snippet", type=str, is_optional=True, is_list=False),
2867
2939
  "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.8.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=uUcnPf4IZjC6ldsu-8CBI6gJMrvg1_EfbTILjLYevW4,2360
2
+ tango/client.py,sha256=hLYbP-lBIBVzSVamOJwSATHqi6ijba6-XtMAB-XTyyo,228019
3
3
  tango/exceptions.py,sha256=B1t27BBxHcPglAfJQyan5khNEYNAyF6Wkp2EmPMdRxM,3858
4
- tango/models.py,sha256=oljb3sihDb2ZQ5bxfjxK5x0p6vrrc032hU_C_15LGFc,53831
4
+ tango/models.py,sha256=KI0I9I9LBetpRUkl0YS7hHbsM2vrOflN83crNSVBUiY,61897
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=41k6rOBMFz-6imGIZ5mXeK26M0s-nmIUZJXQeDjQFH0,78508
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=OuwuyixXplySy5SM0FqQ0KKPOJJ490zyOi3wzP3DEOQ,139031
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.8.0.dist-info/METADATA,sha256=46doMnyaajvAdOUSTAHyjznVIKJ6Nepwgu4CpEn8SuQ,21779
20
+ tango_python-1.8.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
21
+ tango_python-1.8.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
22
+ tango_python-1.8.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
23
+ tango_python-1.8.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