tango-python 1.5.0__py3-none-any.whl → 1.6.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 +7 -1
- tango/client.py +453 -2
- tango/models.py +164 -0
- tango/shapes/generated_overlay.py +506 -45
- {tango_python-1.5.0.dist-info → tango_python-1.6.0.dist-info}/METADATA +2 -2
- {tango_python-1.5.0.dist-info → tango_python-1.6.0.dist-info}/RECORD +9 -9
- {tango_python-1.5.0.dist-info → tango_python-1.6.0.dist-info}/WHEEL +1 -1
- {tango_python-1.5.0.dist-info → tango_python-1.6.0.dist-info}/entry_points.txt +0 -0
- {tango_python-1.5.0.dist-info → tango_python-1.6.0.dist-info}/licenses/LICENSE +0 -0
tango/__init__.py
CHANGED
|
@@ -24,6 +24,9 @@ from .models import (
|
|
|
24
24
|
SbirTopic,
|
|
25
25
|
SearchFilters,
|
|
26
26
|
ShapeConfig,
|
|
27
|
+
SledForecast,
|
|
28
|
+
SledOpportunity,
|
|
29
|
+
SledOpportunityRevision,
|
|
27
30
|
ValidateResult,
|
|
28
31
|
Vehicle,
|
|
29
32
|
VehicleMetrics,
|
|
@@ -50,7 +53,7 @@ from .webhooks import (
|
|
|
50
53
|
)
|
|
51
54
|
from .webhooks.receiver import Delivery, WebhookReceiver
|
|
52
55
|
|
|
53
|
-
__version__ = "1.
|
|
56
|
+
__version__ = "1.6.0"
|
|
54
57
|
__all__ = [
|
|
55
58
|
"TangoClient",
|
|
56
59
|
"TangoAPIError",
|
|
@@ -73,6 +76,9 @@ __all__ = [
|
|
|
73
76
|
"PaginatedResponse",
|
|
74
77
|
"SearchFilters",
|
|
75
78
|
"ShapeConfig",
|
|
79
|
+
"SledForecast",
|
|
80
|
+
"SledOpportunity",
|
|
81
|
+
"SledOpportunityRevision",
|
|
76
82
|
"ValidateResult",
|
|
77
83
|
"Vehicle",
|
|
78
84
|
"VehicleMetrics",
|
tango/client.py
CHANGED
|
@@ -46,6 +46,9 @@ from tango.models import (
|
|
|
46
46
|
SbirTopic,
|
|
47
47
|
SearchFilters,
|
|
48
48
|
ShapeConfig,
|
|
49
|
+
SledForecast,
|
|
50
|
+
SledOpportunity,
|
|
51
|
+
SledOpportunityRevision,
|
|
49
52
|
Subaward,
|
|
50
53
|
ValidateResult,
|
|
51
54
|
Vehicle,
|
|
@@ -1596,12 +1599,15 @@ class TangoClient:
|
|
|
1596
1599
|
cio_rating: int | None = None,
|
|
1597
1600
|
cio_rating_max: int | None = None,
|
|
1598
1601
|
performance_risk: bool | None = None,
|
|
1602
|
+
previous_uii: str | None = None,
|
|
1599
1603
|
) -> PaginatedResponse:
|
|
1600
1604
|
"""List federal IT investments from the IT Dashboard (`/api/itdashboard/`).
|
|
1601
1605
|
|
|
1602
1606
|
Filters are tier-gated by the API:
|
|
1603
1607
|
|
|
1604
|
-
- **Free**: ``search`` (full-text across UII, title, description, agency,
|
|
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
|
|
1605
1611
|
- **Pro**: ``agency_code``, ``type_of_investment``,
|
|
1606
1612
|
``updated_time_after`` / ``updated_time_before``
|
|
1607
1613
|
- **Business+**: ``agency_name`` (text), ``cio_rating``,
|
|
@@ -1633,6 +1639,7 @@ class TangoClient:
|
|
|
1633
1639
|
("cio_rating", cio_rating),
|
|
1634
1640
|
("cio_rating_max", cio_rating_max),
|
|
1635
1641
|
("performance_risk", performance_risk),
|
|
1642
|
+
("previous_uii", previous_uii),
|
|
1636
1643
|
):
|
|
1637
1644
|
if val is None:
|
|
1638
1645
|
continue
|
|
@@ -2081,7 +2088,7 @@ class TangoClient:
|
|
|
2081
2088
|
name: Entity name filter
|
|
2082
2089
|
psc: PSC code filter
|
|
2083
2090
|
purpose_of_registration_code: Purpose of registration code
|
|
2084
|
-
socioeconomic: Socioeconomic status filter
|
|
2091
|
+
socioeconomic: Socioeconomic status filter; takes SAM business-type codes (e.g. "OY" Black American Owned, "A6" SBA-certified 8(a), "A2" Woman Owned — see `GET /api/business_types/`), not set-aside codes, and accepts pipe-separated values for OR semantics, e.g. ``socioeconomic="OY|A2"``
|
|
2085
2092
|
state: State filter
|
|
2086
2093
|
total_awards_obligated_gte: Total awards obligated >=
|
|
2087
2094
|
total_awards_obligated_lte: Total awards obligated <=
|
|
@@ -3464,6 +3471,450 @@ class TangoClient:
|
|
|
3464
3471
|
data = self._get(f"/api/sbir/solicitations/{solicitation_id}/", params)
|
|
3465
3472
|
return self._parse_response_with_shape(data, shape, SbirSolicitation, flat, flat_lists)
|
|
3466
3473
|
|
|
3474
|
+
# ============================================================================
|
|
3475
|
+
# State, local and education (SLED) procurement
|
|
3476
|
+
# ============================================================================
|
|
3477
|
+
|
|
3478
|
+
def list_sled_opportunities(
|
|
3479
|
+
self,
|
|
3480
|
+
page: int = 1,
|
|
3481
|
+
limit: int = 25,
|
|
3482
|
+
shape: str | None = None,
|
|
3483
|
+
flat: bool = False,
|
|
3484
|
+
flat_lists: bool = False,
|
|
3485
|
+
state: str | None = None,
|
|
3486
|
+
jurisdiction: str | None = None,
|
|
3487
|
+
status: str | None = None,
|
|
3488
|
+
active: bool | None = None,
|
|
3489
|
+
agency: str | None = None,
|
|
3490
|
+
solicitation_number: str | None = None,
|
|
3491
|
+
solicitation_type: str | None = None,
|
|
3492
|
+
has_documents: bool | None = None,
|
|
3493
|
+
revision_kind: str | None = None,
|
|
3494
|
+
naics: str | None = None,
|
|
3495
|
+
nigp: str | None = None,
|
|
3496
|
+
unspsc: str | None = None,
|
|
3497
|
+
category: str | None = None,
|
|
3498
|
+
category_code: str | None = None,
|
|
3499
|
+
posted_after: str | None = None,
|
|
3500
|
+
posted_before: str | None = None,
|
|
3501
|
+
response_deadline_after: str | None = None,
|
|
3502
|
+
response_deadline_before: str | None = None,
|
|
3503
|
+
first_seen_after: str | None = None,
|
|
3504
|
+
first_seen_before: str | None = None,
|
|
3505
|
+
change_seen_after: str | None = None,
|
|
3506
|
+
modified_after: str | None = None,
|
|
3507
|
+
modified_before: str | None = None,
|
|
3508
|
+
platform: str | None = None,
|
|
3509
|
+
native_id: str | None = None,
|
|
3510
|
+
external_id: str | None = None,
|
|
3511
|
+
search: str | None = None,
|
|
3512
|
+
ordering: str | None = None,
|
|
3513
|
+
) -> PaginatedResponse:
|
|
3514
|
+
"""
|
|
3515
|
+
List state, local and education (SLED) solicitations.
|
|
3516
|
+
|
|
3517
|
+
API reference: https://tango.makegov.com/docs/api-reference/sled.md
|
|
3518
|
+
|
|
3519
|
+
Beta. Coverage is partial and grows one jurisdiction at a time, so a thin
|
|
3520
|
+
per-state result is at least as likely to be a portal Tango does not read
|
|
3521
|
+
as a quiet market. Call :meth:`get_sled_coverage` before treating a
|
|
3522
|
+
per-state count as market size.
|
|
3523
|
+
|
|
3524
|
+
Args:
|
|
3525
|
+
page: Page number
|
|
3526
|
+
limit: Results per page (max 100)
|
|
3527
|
+
shape: Response shape string (defaults to minimal shape)
|
|
3528
|
+
flat: If True, flatten nested objects in shaped response
|
|
3529
|
+
flat_lists: If True, flatten arrays using indexed keys
|
|
3530
|
+
state: Two-letter state or territory code. Multi-value: ``TX|OK``
|
|
3531
|
+
jurisdiction: Level of government — ``state``, ``local``,
|
|
3532
|
+
``education``, or ``unknown`` for aggregator rows that cannot
|
|
3533
|
+
tell state from local
|
|
3534
|
+
status: Tango-derived liveness — ``open``, ``closed``, ``awarded``,
|
|
3535
|
+
``cancelled`` or ``unknown``. **Passing neither this nor**
|
|
3536
|
+
``active`` **returns open solicitations only.** ``unknown``
|
|
3537
|
+
(standing rosters, dateless RFIs) is hidden by that default —
|
|
3538
|
+
reach it with ``status="open|unknown"``
|
|
3539
|
+
active: Sugar for federal-shaped callers. ``True`` is
|
|
3540
|
+
``status="open"``; ``False`` is its complement, so it includes
|
|
3541
|
+
``unknown``
|
|
3542
|
+
agency: Substring match on the buyer's published text (min 2
|
|
3543
|
+
characters). There is no code resolution behind it — state
|
|
3544
|
+
agencies have no entry in the federal organization tree
|
|
3545
|
+
solicitation_number: The number a human would quote. Null on roughly
|
|
3546
|
+
a third of the corpus, where the portal publishes none
|
|
3547
|
+
solicitation_type: ``rfp``, ``ifb``, ``rfq``, ``rfi``, ``itb``,
|
|
3548
|
+
``sole_source``, ``grant`` or ``other``. ``null`` (the portal
|
|
3549
|
+
states no type) is a distinct answer from ``other``
|
|
3550
|
+
has_documents: Whether the solicitation advertises at least one
|
|
3551
|
+
document
|
|
3552
|
+
revision_kind: Kind of the most recent substantive revision —
|
|
3553
|
+
``deadline_change``, ``status_change``, ``documents_added``,
|
|
3554
|
+
``documents_removed``, ``documents_replaced``, ``title_change``
|
|
3555
|
+
or ``content_change``
|
|
3556
|
+
naics: Exact match within the ``naics`` category scheme. Thin on
|
|
3557
|
+
purpose: scheme tagging is mid-migration, so only a small share
|
|
3558
|
+
of entries are tagged NAICS. Use ``category_code`` instead unless
|
|
3559
|
+
you need scheme precision
|
|
3560
|
+
nigp: Exact match within the ``nigp`` scheme
|
|
3561
|
+
unspsc: Exact match within the ``unspsc`` scheme
|
|
3562
|
+
category: Exact match within the ``text`` scheme, where the code is
|
|
3563
|
+
the portal's own human label
|
|
3564
|
+
category_code: Match a code under ANY scheme, including the untagged
|
|
3565
|
+
pre-migration strings. The escape hatch when a scheme-specific
|
|
3566
|
+
filter returns less than you expected
|
|
3567
|
+
posted_after: Posted date on or after (YYYY-MM-DD)
|
|
3568
|
+
posted_before: Posted date on or before (YYYY-MM-DD)
|
|
3569
|
+
response_deadline_after: Response deadline on or after (YYYY-MM-DD)
|
|
3570
|
+
response_deadline_before: Response deadline on or before (YYYY-MM-DD)
|
|
3571
|
+
first_seen_after: When Tango FIRST OBSERVED the solicitation, on or
|
|
3572
|
+
after (YYYY-MM-DD). The polling primitive
|
|
3573
|
+
first_seen_before: First observed on or before (YYYY-MM-DD)
|
|
3574
|
+
change_seen_after: When Tango OBSERVED the last substantive change,
|
|
3575
|
+
on or after (YYYY-MM-DD). A scrape date, not an amendment date
|
|
3576
|
+
modified_after: Tango row last changed on or after (YYYY-MM-DD)
|
|
3577
|
+
modified_before: Tango row last changed on or before (YYYY-MM-DD)
|
|
3578
|
+
platform: Support filter identifying the source portal's platform
|
|
3579
|
+
family. Not in any response shape, and not a stable value
|
|
3580
|
+
native_id: Support filter — the portal's own identifier
|
|
3581
|
+
external_id: Support filter — Tango's opaque lake key. At most 500
|
|
3582
|
+
values
|
|
3583
|
+
search: Ranked full-text search over title, agency, identifiers,
|
|
3584
|
+
category labels and description, widened by the solicitations
|
|
3585
|
+
whose ATTACHMENT text matched (min 2 characters). Adds a
|
|
3586
|
+
``snippet`` to rows that matched on their description
|
|
3587
|
+
ordering: Sort field — ``rank``, ``response_deadline``,
|
|
3588
|
+
``posted_date``, ``first_seen_at``, ``last_seen_at``,
|
|
3589
|
+
``last_change_seen_at``, ``modified``. ``rank`` requires a
|
|
3590
|
+
non-empty ``search``
|
|
3591
|
+
"""
|
|
3592
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
3593
|
+
|
|
3594
|
+
if shape is None:
|
|
3595
|
+
shape = ShapeConfig.SLED_OPPORTUNITIES_MINIMAL
|
|
3596
|
+
if shape:
|
|
3597
|
+
params["shape"] = shape
|
|
3598
|
+
if flat:
|
|
3599
|
+
params["flat"] = "true"
|
|
3600
|
+
if flat_lists:
|
|
3601
|
+
params["flat_lists"] = "true"
|
|
3602
|
+
|
|
3603
|
+
for key, val in (
|
|
3604
|
+
("state", state),
|
|
3605
|
+
("jurisdiction", jurisdiction),
|
|
3606
|
+
("status", status),
|
|
3607
|
+
("active", active),
|
|
3608
|
+
("agency", agency),
|
|
3609
|
+
("solicitation_number", solicitation_number),
|
|
3610
|
+
("solicitation_type", solicitation_type),
|
|
3611
|
+
("has_documents", has_documents),
|
|
3612
|
+
("revision_kind", revision_kind),
|
|
3613
|
+
("naics", naics),
|
|
3614
|
+
("nigp", nigp),
|
|
3615
|
+
("unspsc", unspsc),
|
|
3616
|
+
("category", category),
|
|
3617
|
+
("category_code", category_code),
|
|
3618
|
+
("posted_after", posted_after),
|
|
3619
|
+
("posted_before", posted_before),
|
|
3620
|
+
("response_deadline_after", response_deadline_after),
|
|
3621
|
+
("response_deadline_before", response_deadline_before),
|
|
3622
|
+
("first_seen_after", first_seen_after),
|
|
3623
|
+
("first_seen_before", first_seen_before),
|
|
3624
|
+
("change_seen_after", change_seen_after),
|
|
3625
|
+
("modified_after", modified_after),
|
|
3626
|
+
("modified_before", modified_before),
|
|
3627
|
+
("platform", platform),
|
|
3628
|
+
("native_id", native_id),
|
|
3629
|
+
("external_id", external_id),
|
|
3630
|
+
("search", search),
|
|
3631
|
+
("ordering", ordering),
|
|
3632
|
+
):
|
|
3633
|
+
if val is not None:
|
|
3634
|
+
params[key] = val
|
|
3635
|
+
|
|
3636
|
+
data = self._get("/api/sled/opportunities/", params)
|
|
3637
|
+
|
|
3638
|
+
results = [
|
|
3639
|
+
self._parse_response_with_shape(item, shape, SledOpportunity, flat, flat_lists)
|
|
3640
|
+
for item in data["results"]
|
|
3641
|
+
]
|
|
3642
|
+
|
|
3643
|
+
return PaginatedResponse(
|
|
3644
|
+
count=data["count"],
|
|
3645
|
+
next=data.get("next"),
|
|
3646
|
+
previous=data.get("previous"),
|
|
3647
|
+
results=results,
|
|
3648
|
+
meta=data.get("meta"),
|
|
3649
|
+
)
|
|
3650
|
+
|
|
3651
|
+
def get_sled_opportunity(
|
|
3652
|
+
self,
|
|
3653
|
+
opportunity_id: str,
|
|
3654
|
+
shape: str | None = None,
|
|
3655
|
+
flat: bool = False,
|
|
3656
|
+
flat_lists: bool = False,
|
|
3657
|
+
) -> Any:
|
|
3658
|
+
"""
|
|
3659
|
+
Get a single SLED solicitation by opportunity_id, whatever its status.
|
|
3660
|
+
|
|
3661
|
+
API reference: https://tango.makegov.com/docs/api-reference/sled.md
|
|
3662
|
+
|
|
3663
|
+
The open-only default applies to the list endpoint, not here — a closed
|
|
3664
|
+
solicitation still resolves on its own URL.
|
|
3665
|
+
|
|
3666
|
+
Args:
|
|
3667
|
+
opportunity_id: Solicitation UUID
|
|
3668
|
+
shape: Response shape string (defaults to the comprehensive shape)
|
|
3669
|
+
flat: If True, flatten nested objects in shaped response
|
|
3670
|
+
flat_lists: If True, flatten arrays using indexed keys
|
|
3671
|
+
"""
|
|
3672
|
+
params: dict[str, Any] = {}
|
|
3673
|
+
if shape is None:
|
|
3674
|
+
shape = ShapeConfig.SLED_OPPORTUNITIES_COMPREHENSIVE
|
|
3675
|
+
if shape:
|
|
3676
|
+
params["shape"] = shape
|
|
3677
|
+
if flat:
|
|
3678
|
+
params["flat"] = "true"
|
|
3679
|
+
if flat_lists:
|
|
3680
|
+
params["flat_lists"] = "true"
|
|
3681
|
+
|
|
3682
|
+
data = self._get(f"/api/sled/opportunities/{opportunity_id}/", params)
|
|
3683
|
+
return self._parse_response_with_shape(data, shape, SledOpportunity, flat, flat_lists)
|
|
3684
|
+
|
|
3685
|
+
def list_sled_opportunity_revisions(
|
|
3686
|
+
self,
|
|
3687
|
+
opportunity_id: str,
|
|
3688
|
+
page: int = 1,
|
|
3689
|
+
limit: int = 25,
|
|
3690
|
+
shape: str | None = None,
|
|
3691
|
+
flat: bool = False,
|
|
3692
|
+
flat_lists: bool = False,
|
|
3693
|
+
kind: str | None = None,
|
|
3694
|
+
source_declared: bool | None = None,
|
|
3695
|
+
observed_after: str | None = None,
|
|
3696
|
+
observed_before: str | None = None,
|
|
3697
|
+
) -> PaginatedResponse:
|
|
3698
|
+
"""
|
|
3699
|
+
List one SLED solicitation's observed revision history.
|
|
3700
|
+
|
|
3701
|
+
API reference: https://tango.makegov.com/docs/api-reference/sled.md
|
|
3702
|
+
|
|
3703
|
+
``observed_at`` is the scrape that saw the change, not the date the agency
|
|
3704
|
+
made it: no state portal emits amendment notices, so resolution is that
|
|
3705
|
+
state's crawl cadence and history starts when Tango began reading the
|
|
3706
|
+
jurisdiction rather than when the solicitation was posted.
|
|
3707
|
+
|
|
3708
|
+
Unlike the ``revisions(*)`` expand on the solicitation, this route serves
|
|
3709
|
+
``enrichment`` rows — Tango's own detail fetch filling in coverage rather
|
|
3710
|
+
than an agency amendment. Pass ``kind="enrichment"`` to see only those.
|
|
3711
|
+
|
|
3712
|
+
Args:
|
|
3713
|
+
opportunity_id: Solicitation UUID
|
|
3714
|
+
page: Page number
|
|
3715
|
+
limit: Results per page (max 100)
|
|
3716
|
+
shape: Response shape string (defaults to minimal shape). ``changes``
|
|
3717
|
+
— the per-field before and after — needs a Small plan; name it
|
|
3718
|
+
explicitly when the caller has one
|
|
3719
|
+
flat: If True, flatten nested objects in shaped response
|
|
3720
|
+
flat_lists: If True, flatten arrays using indexed keys
|
|
3721
|
+
kind: Revision kind, plus ``enrichment``. Multi-value: use ``|``
|
|
3722
|
+
source_declared: Whether the portal's own amendment marker moved at
|
|
3723
|
+
this emission. True on about 5% of revisions; everything else is
|
|
3724
|
+
Tango inferring the change from the diff
|
|
3725
|
+
observed_after: Observed on or after (YYYY-MM-DD)
|
|
3726
|
+
observed_before: Observed on or before (YYYY-MM-DD)
|
|
3727
|
+
"""
|
|
3728
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
3729
|
+
|
|
3730
|
+
if shape is None:
|
|
3731
|
+
shape = ShapeConfig.SLED_REVISIONS_MINIMAL
|
|
3732
|
+
if shape:
|
|
3733
|
+
params["shape"] = shape
|
|
3734
|
+
if flat:
|
|
3735
|
+
params["flat"] = "true"
|
|
3736
|
+
if flat_lists:
|
|
3737
|
+
params["flat_lists"] = "true"
|
|
3738
|
+
|
|
3739
|
+
for key, val in (
|
|
3740
|
+
("kind", kind),
|
|
3741
|
+
("source_declared", source_declared),
|
|
3742
|
+
("observed_after", observed_after),
|
|
3743
|
+
("observed_before", observed_before),
|
|
3744
|
+
):
|
|
3745
|
+
if val is not None:
|
|
3746
|
+
params[key] = val
|
|
3747
|
+
|
|
3748
|
+
data = self._get(f"/api/sled/opportunities/{opportunity_id}/revisions/", params)
|
|
3749
|
+
|
|
3750
|
+
results = [
|
|
3751
|
+
self._parse_response_with_shape(item, shape, SledOpportunityRevision, flat, flat_lists)
|
|
3752
|
+
for item in data["results"]
|
|
3753
|
+
]
|
|
3754
|
+
|
|
3755
|
+
return PaginatedResponse(
|
|
3756
|
+
count=data["count"],
|
|
3757
|
+
next=data.get("next"),
|
|
3758
|
+
previous=data.get("previous"),
|
|
3759
|
+
results=results,
|
|
3760
|
+
meta=data.get("meta"),
|
|
3761
|
+
)
|
|
3762
|
+
|
|
3763
|
+
def get_sled_coverage(self) -> dict[str, Any]:
|
|
3764
|
+
"""
|
|
3765
|
+
Get the per-state SLED coverage rollup.
|
|
3766
|
+
|
|
3767
|
+
API reference: https://tango.makegov.com/docs/api-reference/sled.md
|
|
3768
|
+
|
|
3769
|
+
Returns corpus totals plus one row per jurisdiction — the total, the count
|
|
3770
|
+
in each of the five statuses, the jurisdiction levels present, and when a
|
|
3771
|
+
solicitation there last changed. It answers one question: whether a thin
|
|
3772
|
+
result for a state is a thin market or a portal Tango does not read.
|
|
3773
|
+
|
|
3774
|
+
Every state row carries all five status buckets whether or not they have
|
|
3775
|
+
rows, so a total and two buckets never invite subtraction. Takes no
|
|
3776
|
+
parameters and is not shaped or paginated.
|
|
3777
|
+
"""
|
|
3778
|
+
return self._get("/api/sled/opportunities/coverage/", {})
|
|
3779
|
+
|
|
3780
|
+
def list_sled_forecasts(
|
|
3781
|
+
self,
|
|
3782
|
+
page: int = 1,
|
|
3783
|
+
limit: int = 25,
|
|
3784
|
+
shape: str | None = None,
|
|
3785
|
+
flat: bool = False,
|
|
3786
|
+
flat_lists: bool = False,
|
|
3787
|
+
state: str | None = None,
|
|
3788
|
+
agency: str | None = None,
|
|
3789
|
+
procurement_category: str | None = None,
|
|
3790
|
+
procurement_method: str | None = None,
|
|
3791
|
+
contract_number: str | None = None,
|
|
3792
|
+
incumbent_name: str | None = None,
|
|
3793
|
+
advertisement_after: str | None = None,
|
|
3794
|
+
advertisement_before: str | None = None,
|
|
3795
|
+
first_seen_after: str | None = None,
|
|
3796
|
+
first_seen_before: str | None = None,
|
|
3797
|
+
modified_after: str | None = None,
|
|
3798
|
+
modified_before: str | None = None,
|
|
3799
|
+
search: str | None = None,
|
|
3800
|
+
ordering: str | None = None,
|
|
3801
|
+
) -> PaginatedResponse:
|
|
3802
|
+
"""
|
|
3803
|
+
List planned state procurements (SLED forecasts).
|
|
3804
|
+
|
|
3805
|
+
API reference: https://tango.makegov.com/docs/api-reference/sled.md
|
|
3806
|
+
|
|
3807
|
+
Forecasts carry no liveness at all — there is no deadline to have passed,
|
|
3808
|
+
so there is no ``status`` filter and no open-only default. Currency is the
|
|
3809
|
+
caller's call from ``estimated_advertisement_date``, which is the START of
|
|
3810
|
+
the published quarter rather than a posting date.
|
|
3811
|
+
|
|
3812
|
+
Args:
|
|
3813
|
+
page: Page number
|
|
3814
|
+
limit: Results per page (max 100)
|
|
3815
|
+
shape: Response shape string (defaults to minimal shape)
|
|
3816
|
+
flat: If True, flatten nested objects in shaped response
|
|
3817
|
+
flat_lists: If True, flatten arrays using indexed keys
|
|
3818
|
+
state: Two-letter state code. Multi-value: use ``|``
|
|
3819
|
+
agency: Substring match on the buyer's published text (min 2
|
|
3820
|
+
characters)
|
|
3821
|
+
procurement_category: The portal's own category word, verbatim
|
|
3822
|
+
procurement_method: The portal's own method word, verbatim
|
|
3823
|
+
contract_number: The number the state expects to award under, where it
|
|
3824
|
+
publishes one in advance
|
|
3825
|
+
incumbent_name: Substring match on the incumbent vendor's name as
|
|
3826
|
+
published. NOT resolved to a Tango entity
|
|
3827
|
+
advertisement_after: Estimated advertisement date on or after
|
|
3828
|
+
(YYYY-MM-DD). Remember the date is a quarter start
|
|
3829
|
+
advertisement_before: Estimated advertisement date on or before
|
|
3830
|
+
(YYYY-MM-DD)
|
|
3831
|
+
first_seen_after: When Tango first observed the forecast, on or after
|
|
3832
|
+
(YYYY-MM-DD)
|
|
3833
|
+
first_seen_before: First observed on or before (YYYY-MM-DD)
|
|
3834
|
+
modified_after: Tango row last changed on or after (YYYY-MM-DD)
|
|
3835
|
+
modified_before: Tango row last changed on or before (YYYY-MM-DD)
|
|
3836
|
+
search: Ranked full-text search over title, agency and description
|
|
3837
|
+
(min 2 characters)
|
|
3838
|
+
ordering: Sort field — ``rank``, ``estimated_advertisement_date``,
|
|
3839
|
+
``first_seen_at``, ``last_seen_at``, ``modified``. ``rank``
|
|
3840
|
+
requires a non-empty ``search``
|
|
3841
|
+
"""
|
|
3842
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
3843
|
+
|
|
3844
|
+
if shape is None:
|
|
3845
|
+
shape = ShapeConfig.SLED_FORECASTS_MINIMAL
|
|
3846
|
+
if shape:
|
|
3847
|
+
params["shape"] = shape
|
|
3848
|
+
if flat:
|
|
3849
|
+
params["flat"] = "true"
|
|
3850
|
+
if flat_lists:
|
|
3851
|
+
params["flat_lists"] = "true"
|
|
3852
|
+
|
|
3853
|
+
for key, val in (
|
|
3854
|
+
("state", state),
|
|
3855
|
+
("agency", agency),
|
|
3856
|
+
("procurement_category", procurement_category),
|
|
3857
|
+
("procurement_method", procurement_method),
|
|
3858
|
+
("contract_number", contract_number),
|
|
3859
|
+
("incumbent_name", incumbent_name),
|
|
3860
|
+
("advertisement_after", advertisement_after),
|
|
3861
|
+
("advertisement_before", advertisement_before),
|
|
3862
|
+
("first_seen_after", first_seen_after),
|
|
3863
|
+
("first_seen_before", first_seen_before),
|
|
3864
|
+
("modified_after", modified_after),
|
|
3865
|
+
("modified_before", modified_before),
|
|
3866
|
+
("search", search),
|
|
3867
|
+
("ordering", ordering),
|
|
3868
|
+
):
|
|
3869
|
+
if val is not None:
|
|
3870
|
+
params[key] = val
|
|
3871
|
+
|
|
3872
|
+
data = self._get("/api/sled/forecasts/", params)
|
|
3873
|
+
|
|
3874
|
+
results = [
|
|
3875
|
+
self._parse_response_with_shape(item, shape, SledForecast, flat, flat_lists)
|
|
3876
|
+
for item in data["results"]
|
|
3877
|
+
]
|
|
3878
|
+
|
|
3879
|
+
return PaginatedResponse(
|
|
3880
|
+
count=data["count"],
|
|
3881
|
+
next=data.get("next"),
|
|
3882
|
+
previous=data.get("previous"),
|
|
3883
|
+
results=results,
|
|
3884
|
+
meta=data.get("meta"),
|
|
3885
|
+
)
|
|
3886
|
+
|
|
3887
|
+
def get_sled_forecast(
|
|
3888
|
+
self,
|
|
3889
|
+
forecast_id: str,
|
|
3890
|
+
shape: str | None = None,
|
|
3891
|
+
flat: bool = False,
|
|
3892
|
+
flat_lists: bool = False,
|
|
3893
|
+
) -> Any:
|
|
3894
|
+
"""
|
|
3895
|
+
Get a single SLED forecast by forecast_id.
|
|
3896
|
+
|
|
3897
|
+
API reference: https://tango.makegov.com/docs/api-reference/sled.md
|
|
3898
|
+
|
|
3899
|
+
Args:
|
|
3900
|
+
forecast_id: Forecast UUID
|
|
3901
|
+
shape: Response shape string (defaults to the comprehensive shape)
|
|
3902
|
+
flat: If True, flatten nested objects in shaped response
|
|
3903
|
+
flat_lists: If True, flatten arrays using indexed keys
|
|
3904
|
+
"""
|
|
3905
|
+
params: dict[str, Any] = {}
|
|
3906
|
+
if shape is None:
|
|
3907
|
+
shape = ShapeConfig.SLED_FORECASTS_COMPREHENSIVE
|
|
3908
|
+
if shape:
|
|
3909
|
+
params["shape"] = shape
|
|
3910
|
+
if flat:
|
|
3911
|
+
params["flat"] = "true"
|
|
3912
|
+
if flat_lists:
|
|
3913
|
+
params["flat_lists"] = "true"
|
|
3914
|
+
|
|
3915
|
+
data = self._get(f"/api/sled/forecasts/{forecast_id}/", params)
|
|
3916
|
+
return self._parse_response_with_shape(data, shape, SledForecast, flat, flat_lists)
|
|
3917
|
+
|
|
3467
3918
|
# ============================================================================
|
|
3468
3919
|
# Budget (federal account x fiscal year rollups)
|
|
3469
3920
|
# ============================================================================
|