tango-python 0.4.4__py3-none-any.whl → 0.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 +18 -1
- tango/client.py +269 -4
- tango/models.py +97 -6
- tango/shapes/explicit_schemas.py +187 -0
- tango/webhooks/__init__.py +27 -0
- tango/webhooks/cli.py +519 -0
- tango/webhooks/receiver.py +232 -0
- tango/webhooks/signing.py +50 -0
- tango/webhooks/simulate.py +102 -0
- {tango_python-0.4.4.dist-info → tango_python-0.6.0.dist-info}/METADATA +50 -2
- tango_python-0.6.0.dist-info/RECORD +22 -0
- tango_python-0.6.0.dist-info/entry_points.txt +2 -0
- tango_python-0.4.4.dist-info/RECORD +0 -16
- {tango_python-0.4.4.dist-info → tango_python-0.6.0.dist-info}/WHEEL +0 -0
- {tango_python-0.4.4.dist-info → tango_python-0.6.0.dist-info}/licenses/LICENSE +0 -0
tango/__init__.py
CHANGED
|
@@ -10,10 +10,13 @@ from .exceptions import (
|
|
|
10
10
|
)
|
|
11
11
|
from .models import (
|
|
12
12
|
GsaElibraryContract,
|
|
13
|
+
ITDashboardInvestment,
|
|
13
14
|
PaginatedResponse,
|
|
14
15
|
RateLimitInfo,
|
|
15
16
|
SearchFilters,
|
|
16
17
|
ShapeConfig,
|
|
18
|
+
Vehicle,
|
|
19
|
+
VehicleMetrics,
|
|
17
20
|
WebhookEndpoint,
|
|
18
21
|
WebhookEventType,
|
|
19
22
|
WebhookEventTypesResponse,
|
|
@@ -27,8 +30,14 @@ from .shapes import (
|
|
|
27
30
|
ShapeParser,
|
|
28
31
|
TypeGenerator,
|
|
29
32
|
)
|
|
33
|
+
from .webhooks import (
|
|
34
|
+
generate_signature,
|
|
35
|
+
parse_signature_header,
|
|
36
|
+
verify_signature,
|
|
37
|
+
)
|
|
38
|
+
from .webhooks.receiver import Delivery, WebhookReceiver
|
|
30
39
|
|
|
31
|
-
__version__ = "0.
|
|
40
|
+
__version__ = "0.6.0"
|
|
32
41
|
__all__ = [
|
|
33
42
|
"TangoClient",
|
|
34
43
|
"TangoAPIError",
|
|
@@ -38,9 +47,12 @@ __all__ = [
|
|
|
38
47
|
"TangoRateLimitError",
|
|
39
48
|
"RateLimitInfo",
|
|
40
49
|
"GsaElibraryContract",
|
|
50
|
+
"ITDashboardInvestment",
|
|
41
51
|
"PaginatedResponse",
|
|
42
52
|
"SearchFilters",
|
|
43
53
|
"ShapeConfig",
|
|
54
|
+
"Vehicle",
|
|
55
|
+
"VehicleMetrics",
|
|
44
56
|
"WebhookEndpoint",
|
|
45
57
|
"WebhookEventType",
|
|
46
58
|
"WebhookEventTypesResponse",
|
|
@@ -51,4 +63,9 @@ __all__ = [
|
|
|
51
63
|
"ModelFactory",
|
|
52
64
|
"TypeGenerator",
|
|
53
65
|
"SchemaRegistry",
|
|
66
|
+
"Delivery",
|
|
67
|
+
"WebhookReceiver",
|
|
68
|
+
"generate_signature",
|
|
69
|
+
"parse_signature_header",
|
|
70
|
+
"verify_signature",
|
|
54
71
|
]
|
tango/client.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Tango API Client"""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import warnings
|
|
4
5
|
from datetime import date, datetime
|
|
5
6
|
from decimal import Decimal
|
|
6
7
|
from typing import Any
|
|
@@ -26,6 +27,7 @@ from tango.models import (
|
|
|
26
27
|
Forecast,
|
|
27
28
|
Grant,
|
|
28
29
|
GsaElibraryContract,
|
|
30
|
+
ITDashboardInvestment,
|
|
29
31
|
Location,
|
|
30
32
|
Notice,
|
|
31
33
|
Opportunity,
|
|
@@ -122,6 +124,7 @@ class TangoClient:
|
|
|
122
124
|
@staticmethod
|
|
123
125
|
def _parse_rate_limit_headers(headers: httpx.Headers) -> RateLimitInfo:
|
|
124
126
|
"""Extract rate limit info from response headers."""
|
|
127
|
+
|
|
125
128
|
def _int_or_none(val: str | None) -> int | None:
|
|
126
129
|
if val is None:
|
|
127
130
|
return None
|
|
@@ -1336,10 +1339,150 @@ class TangoClient:
|
|
|
1336
1339
|
data, shape, GsaElibraryContract, flat, flat_lists, joiner=joiner
|
|
1337
1340
|
)
|
|
1338
1341
|
|
|
1342
|
+
# ============================================================================
|
|
1343
|
+
# IT Dashboard Investments
|
|
1344
|
+
# ============================================================================
|
|
1345
|
+
|
|
1346
|
+
def list_itdashboard_investments(
|
|
1347
|
+
self,
|
|
1348
|
+
page: int = 1,
|
|
1349
|
+
limit: int = 25,
|
|
1350
|
+
shape: str | None = None,
|
|
1351
|
+
flat: bool = False,
|
|
1352
|
+
flat_lists: bool = False,
|
|
1353
|
+
joiner: str = ".",
|
|
1354
|
+
search: str | None = None,
|
|
1355
|
+
agency_code: int | None = None,
|
|
1356
|
+
agency_name: str | None = None,
|
|
1357
|
+
type_of_investment: str | None = None,
|
|
1358
|
+
updated_time_after: str | date | datetime | None = None,
|
|
1359
|
+
updated_time_before: str | date | datetime | None = None,
|
|
1360
|
+
cio_rating: int | None = None,
|
|
1361
|
+
cio_rating_max: int | None = None,
|
|
1362
|
+
performance_risk: bool | None = None,
|
|
1363
|
+
) -> PaginatedResponse:
|
|
1364
|
+
"""List federal IT investments from the IT Dashboard (`/api/itdashboard/`).
|
|
1365
|
+
|
|
1366
|
+
Filters are tier-gated by the API:
|
|
1367
|
+
|
|
1368
|
+
- **Free**: ``search`` (full-text across UII, title, description, agency, bureau)
|
|
1369
|
+
- **Pro**: ``agency_code``, ``type_of_investment``,
|
|
1370
|
+
``updated_time_after`` / ``updated_time_before``
|
|
1371
|
+
- **Business+**: ``agency_name`` (text), ``cio_rating``,
|
|
1372
|
+
``cio_rating_max``, ``performance_risk``
|
|
1373
|
+
|
|
1374
|
+
Hitting a gated filter on a lower tier returns a 403 with upgrade info.
|
|
1375
|
+
|
|
1376
|
+
CIO ratings: 1=High Risk, 2=Moderately High, 3=Medium, 4=Moderately Low, 5=Low.
|
|
1377
|
+
``performance_risk=True`` returns investments with at least one NOT MET metric.
|
|
1378
|
+
"""
|
|
1379
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1380
|
+
if shape is None:
|
|
1381
|
+
shape = ShapeConfig.ITDASHBOARD_INVESTMENTS_MINIMAL
|
|
1382
|
+
if shape:
|
|
1383
|
+
params["shape"] = shape
|
|
1384
|
+
if flat:
|
|
1385
|
+
params["flat"] = "true"
|
|
1386
|
+
if joiner:
|
|
1387
|
+
params["joiner"] = joiner
|
|
1388
|
+
if flat_lists:
|
|
1389
|
+
params["flat_lists"] = "true"
|
|
1390
|
+
for k, val in (
|
|
1391
|
+
("search", search),
|
|
1392
|
+
("agency_code", agency_code),
|
|
1393
|
+
("agency_name", agency_name),
|
|
1394
|
+
("type_of_investment", type_of_investment),
|
|
1395
|
+
("updated_time_after", updated_time_after),
|
|
1396
|
+
("updated_time_before", updated_time_before),
|
|
1397
|
+
("cio_rating", cio_rating),
|
|
1398
|
+
("cio_rating_max", cio_rating_max),
|
|
1399
|
+
("performance_risk", performance_risk),
|
|
1400
|
+
):
|
|
1401
|
+
if val is None:
|
|
1402
|
+
continue
|
|
1403
|
+
if isinstance(val, bool):
|
|
1404
|
+
params[k] = "true" if val else "false"
|
|
1405
|
+
elif isinstance(val, (date, datetime)):
|
|
1406
|
+
params[k] = val.isoformat()
|
|
1407
|
+
else:
|
|
1408
|
+
params[k] = val
|
|
1409
|
+
data = self._get("/api/itdashboard/", params)
|
|
1410
|
+
results = [
|
|
1411
|
+
self._parse_response_with_shape(
|
|
1412
|
+
obj, shape, ITDashboardInvestment, flat, flat_lists, joiner=joiner
|
|
1413
|
+
)
|
|
1414
|
+
for obj in data.get("results", [])
|
|
1415
|
+
]
|
|
1416
|
+
return PaginatedResponse(
|
|
1417
|
+
count=data.get("count", 0),
|
|
1418
|
+
next=data.get("next"),
|
|
1419
|
+
previous=data.get("previous"),
|
|
1420
|
+
results=results,
|
|
1421
|
+
)
|
|
1422
|
+
|
|
1423
|
+
def get_itdashboard_investment(
|
|
1424
|
+
self,
|
|
1425
|
+
uii: str,
|
|
1426
|
+
shape: str | None = None,
|
|
1427
|
+
flat: bool = False,
|
|
1428
|
+
flat_lists: bool = False,
|
|
1429
|
+
joiner: str = ".",
|
|
1430
|
+
) -> Any:
|
|
1431
|
+
"""Get a single IT Dashboard investment by UII (`/api/itdashboard/{uii}/`)."""
|
|
1432
|
+
params: dict[str, Any] = {}
|
|
1433
|
+
if shape is None:
|
|
1434
|
+
shape = ShapeConfig.ITDASHBOARD_INVESTMENTS_COMPREHENSIVE
|
|
1435
|
+
if shape:
|
|
1436
|
+
params["shape"] = shape
|
|
1437
|
+
if flat:
|
|
1438
|
+
params["flat"] = "true"
|
|
1439
|
+
if joiner:
|
|
1440
|
+
params["joiner"] = joiner
|
|
1441
|
+
if flat_lists:
|
|
1442
|
+
params["flat_lists"] = "true"
|
|
1443
|
+
data = self._get(f"/api/itdashboard/{uii}/", params)
|
|
1444
|
+
return self._parse_response_with_shape(
|
|
1445
|
+
data, shape, ITDashboardInvestment, flat, flat_lists, joiner=joiner
|
|
1446
|
+
)
|
|
1447
|
+
|
|
1339
1448
|
# ============================================================================
|
|
1340
1449
|
# Vehicles (Awards)
|
|
1341
1450
|
# ============================================================================
|
|
1342
1451
|
|
|
1452
|
+
@staticmethod
|
|
1453
|
+
def _warn_deprecated_vehicle_shape(shape: str | None) -> None:
|
|
1454
|
+
# Upstream sends `Deprecation: true` for these fields/expansions; warn
|
|
1455
|
+
# callers who request them explicitly so they have time to migrate
|
|
1456
|
+
# before tango publishes a Sunset timeline.
|
|
1457
|
+
from tango.shapes.explicit_schemas import DEPRECATED_VEHICLE_SHAPE_FIELDS
|
|
1458
|
+
|
|
1459
|
+
if not shape:
|
|
1460
|
+
return
|
|
1461
|
+
# Match top-level field tokens, ignoring nesting inside parentheses.
|
|
1462
|
+
depth = 0
|
|
1463
|
+
token = ""
|
|
1464
|
+
tokens: list[str] = []
|
|
1465
|
+
for ch in shape:
|
|
1466
|
+
if ch == "(":
|
|
1467
|
+
depth += 1
|
|
1468
|
+
elif ch == ")":
|
|
1469
|
+
depth = max(0, depth - 1)
|
|
1470
|
+
elif ch == "," and depth == 0:
|
|
1471
|
+
tokens.append(token.strip())
|
|
1472
|
+
token = ""
|
|
1473
|
+
continue
|
|
1474
|
+
token += ch
|
|
1475
|
+
tokens.append(token.strip())
|
|
1476
|
+
used = {t.split("(", 1)[0] for t in tokens} & DEPRECATED_VEHICLE_SHAPE_FIELDS
|
|
1477
|
+
if used:
|
|
1478
|
+
warnings.warn(
|
|
1479
|
+
f"Vehicle shape field(s) {sorted(used)!r} are deprecated upstream "
|
|
1480
|
+
"and may be removed in a future tango API version. The API currently "
|
|
1481
|
+
"returns a `Deprecation: true` header for these.",
|
|
1482
|
+
DeprecationWarning,
|
|
1483
|
+
stacklevel=3,
|
|
1484
|
+
)
|
|
1485
|
+
|
|
1343
1486
|
def list_vehicles(
|
|
1344
1487
|
self,
|
|
1345
1488
|
page: int = 1,
|
|
@@ -1349,12 +1492,46 @@ class TangoClient:
|
|
|
1349
1492
|
flat_lists: bool = False,
|
|
1350
1493
|
joiner: str = ".",
|
|
1351
1494
|
search: str | None = None,
|
|
1495
|
+
vehicle_type: str | None = None,
|
|
1496
|
+
type_of_idc: str | None = None,
|
|
1497
|
+
contract_type: str | None = None,
|
|
1498
|
+
set_aside: str | None = None,
|
|
1499
|
+
who_can_use: str | None = None,
|
|
1500
|
+
naics_code: int | None = None,
|
|
1501
|
+
psc_code: str | None = None,
|
|
1502
|
+
program_acronym: str | None = None,
|
|
1503
|
+
agency: str | None = None,
|
|
1504
|
+
organization_id: str | None = None,
|
|
1505
|
+
total_obligated_min: float | int | Decimal | None = None,
|
|
1506
|
+
total_obligated_max: float | int | Decimal | None = None,
|
|
1507
|
+
idv_count_min: int | None = None,
|
|
1508
|
+
idv_count_max: int | None = None,
|
|
1509
|
+
order_count_min: int | None = None,
|
|
1510
|
+
order_count_max: int | None = None,
|
|
1511
|
+
fiscal_year: int | None = None,
|
|
1512
|
+
award_date_after: str | date | datetime | None = None,
|
|
1513
|
+
award_date_before: str | date | datetime | None = None,
|
|
1514
|
+
last_date_to_order_after: str | date | datetime | None = None,
|
|
1515
|
+
last_date_to_order_before: str | date | datetime | None = None,
|
|
1516
|
+
ordering: str | None = None,
|
|
1352
1517
|
) -> PaginatedResponse:
|
|
1353
|
-
"""List Vehicles (solicitation-centric groupings of IDVs).
|
|
1518
|
+
"""List Vehicles (solicitation-centric groupings of IDVs).
|
|
1519
|
+
|
|
1520
|
+
Multi-value filters (``vehicle_type``, ``type_of_idc``, ``contract_type``,
|
|
1521
|
+
``set_aside``) accept pipe-separated values for OR semantics, e.g.
|
|
1522
|
+
``vehicle_type="A|B|C"``.
|
|
1523
|
+
|
|
1524
|
+
``ordering`` accepts: ``vehicle_obligations``, ``latest_award_date``,
|
|
1525
|
+
``total_obligated``, ``award_date``, ``last_date_to_order``,
|
|
1526
|
+
``fiscal_year``, ``idv_count``, ``order_count``. Prefix with ``-`` for
|
|
1527
|
+
descending.
|
|
1528
|
+
"""
|
|
1354
1529
|
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1355
1530
|
|
|
1356
1531
|
if shape is None:
|
|
1357
1532
|
shape = ShapeConfig.VEHICLES_MINIMAL
|
|
1533
|
+
else:
|
|
1534
|
+
self._warn_deprecated_vehicle_shape(shape)
|
|
1358
1535
|
if shape:
|
|
1359
1536
|
params["shape"] = shape
|
|
1360
1537
|
if flat:
|
|
@@ -1364,8 +1541,37 @@ class TangoClient:
|
|
|
1364
1541
|
if flat_lists:
|
|
1365
1542
|
params["flat_lists"] = "true"
|
|
1366
1543
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1544
|
+
for k, val in (
|
|
1545
|
+
("search", search),
|
|
1546
|
+
("vehicle_type", vehicle_type),
|
|
1547
|
+
("type_of_idc", type_of_idc),
|
|
1548
|
+
("contract_type", contract_type),
|
|
1549
|
+
("set_aside", set_aside),
|
|
1550
|
+
("who_can_use", who_can_use),
|
|
1551
|
+
("naics_code", naics_code),
|
|
1552
|
+
("psc_code", psc_code),
|
|
1553
|
+
("program_acronym", program_acronym),
|
|
1554
|
+
("agency", agency),
|
|
1555
|
+
("organization_id", organization_id),
|
|
1556
|
+
("total_obligated_min", total_obligated_min),
|
|
1557
|
+
("total_obligated_max", total_obligated_max),
|
|
1558
|
+
("idv_count_min", idv_count_min),
|
|
1559
|
+
("idv_count_max", idv_count_max),
|
|
1560
|
+
("order_count_min", order_count_min),
|
|
1561
|
+
("order_count_max", order_count_max),
|
|
1562
|
+
("fiscal_year", fiscal_year),
|
|
1563
|
+
("award_date_after", award_date_after),
|
|
1564
|
+
("award_date_before", award_date_before),
|
|
1565
|
+
("last_date_to_order_after", last_date_to_order_after),
|
|
1566
|
+
("last_date_to_order_before", last_date_to_order_before),
|
|
1567
|
+
("ordering", ordering),
|
|
1568
|
+
):
|
|
1569
|
+
if val is None:
|
|
1570
|
+
continue
|
|
1571
|
+
if isinstance(val, (date, datetime)):
|
|
1572
|
+
params[k] = val.isoformat()
|
|
1573
|
+
else:
|
|
1574
|
+
params[k] = val
|
|
1369
1575
|
|
|
1370
1576
|
data = self._get("/api/vehicles/", params)
|
|
1371
1577
|
|
|
@@ -1397,6 +1603,8 @@ class TangoClient:
|
|
|
1397
1603
|
|
|
1398
1604
|
if shape is None:
|
|
1399
1605
|
shape = ShapeConfig.VEHICLES_COMPREHENSIVE
|
|
1606
|
+
else:
|
|
1607
|
+
self._warn_deprecated_vehicle_shape(shape)
|
|
1400
1608
|
if shape:
|
|
1401
1609
|
params["shape"] = shape
|
|
1402
1610
|
if flat:
|
|
@@ -1424,8 +1632,14 @@ class TangoClient:
|
|
|
1424
1632
|
flat: bool = False,
|
|
1425
1633
|
flat_lists: bool = False,
|
|
1426
1634
|
joiner: str = ".",
|
|
1635
|
+
search: str | None = None,
|
|
1427
1636
|
) -> PaginatedResponse:
|
|
1428
|
-
"""List the IDV awardees for a Vehicle (`/api/vehicles/{uuid}/awardees/`).
|
|
1637
|
+
"""List the IDV awardees for a Vehicle (`/api/vehicles/{uuid}/awardees/`).
|
|
1638
|
+
|
|
1639
|
+
``search`` runs entity-aware full-text search across IDV fields
|
|
1640
|
+
(PIID, key, solicitation_identifier, NAICS, PSC, idv_type,
|
|
1641
|
+
fiscal_year) and recipient entity details (name, address).
|
|
1642
|
+
"""
|
|
1429
1643
|
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1430
1644
|
|
|
1431
1645
|
if shape is None:
|
|
@@ -1439,6 +1653,9 @@ class TangoClient:
|
|
|
1439
1653
|
if flat_lists:
|
|
1440
1654
|
params["flat_lists"] = "true"
|
|
1441
1655
|
|
|
1656
|
+
if search:
|
|
1657
|
+
params["search"] = search
|
|
1658
|
+
|
|
1442
1659
|
data = self._get(f"/api/vehicles/{uuid}/awardees/", params)
|
|
1443
1660
|
|
|
1444
1661
|
results = [
|
|
@@ -1453,6 +1670,54 @@ class TangoClient:
|
|
|
1453
1670
|
results=results,
|
|
1454
1671
|
)
|
|
1455
1672
|
|
|
1673
|
+
def list_vehicle_orders(
|
|
1674
|
+
self,
|
|
1675
|
+
uuid: str,
|
|
1676
|
+
page: int = 1,
|
|
1677
|
+
limit: int = 25,
|
|
1678
|
+
shape: str | None = None,
|
|
1679
|
+
flat: bool = False,
|
|
1680
|
+
flat_lists: bool = False,
|
|
1681
|
+
joiner: str = ".",
|
|
1682
|
+
ordering: str | None = None,
|
|
1683
|
+
) -> PaginatedResponse:
|
|
1684
|
+
"""List task orders under a Vehicle's IDVs (``/api/vehicles/{uuid}/orders/``).
|
|
1685
|
+
|
|
1686
|
+
Args:
|
|
1687
|
+
ordering: Server-side sort. Allowed: ``award_date`` (default),
|
|
1688
|
+
``obligated``, ``total_contract_value``. Prefix with ``-`` for
|
|
1689
|
+
descending.
|
|
1690
|
+
"""
|
|
1691
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1692
|
+
|
|
1693
|
+
if shape is None:
|
|
1694
|
+
shape = ShapeConfig.VEHICLE_ORDERS_MINIMAL
|
|
1695
|
+
if shape:
|
|
1696
|
+
params["shape"] = shape
|
|
1697
|
+
if flat:
|
|
1698
|
+
params["flat"] = "true"
|
|
1699
|
+
if joiner:
|
|
1700
|
+
params["joiner"] = joiner
|
|
1701
|
+
if flat_lists:
|
|
1702
|
+
params["flat_lists"] = "true"
|
|
1703
|
+
|
|
1704
|
+
if ordering:
|
|
1705
|
+
params["ordering"] = ordering
|
|
1706
|
+
|
|
1707
|
+
data = self._get(f"/api/vehicles/{uuid}/orders/", params)
|
|
1708
|
+
|
|
1709
|
+
results = [
|
|
1710
|
+
self._parse_response_with_shape(order, shape, Contract, flat, flat_lists, joiner=joiner)
|
|
1711
|
+
for order in data["results"]
|
|
1712
|
+
]
|
|
1713
|
+
|
|
1714
|
+
return PaginatedResponse(
|
|
1715
|
+
count=data["count"],
|
|
1716
|
+
next=data.get("next"),
|
|
1717
|
+
previous=data.get("previous"),
|
|
1718
|
+
results=results,
|
|
1719
|
+
)
|
|
1720
|
+
|
|
1456
1721
|
# Business Types endpoints
|
|
1457
1722
|
def list_business_types(self, page: int = 1, limit: int = 25) -> PaginatedResponse:
|
|
1458
1723
|
"""List business types"""
|
tango/models.py
CHANGED
|
@@ -367,6 +367,45 @@ class GsaElibraryContract:
|
|
|
367
367
|
sins: list[str] | None = None
|
|
368
368
|
|
|
369
369
|
|
|
370
|
+
@dataclass
|
|
371
|
+
class ITDashboardInvestment:
|
|
372
|
+
"""Schema definition for IT Dashboard Investment (not used for instances)
|
|
373
|
+
|
|
374
|
+
Federal IT investment from itdashboard.gov, exposed at /api/itdashboard/.
|
|
375
|
+
Identified by ``uii`` (Unique Investment Identifier).
|
|
376
|
+
|
|
377
|
+
Tier-gated shape expansions:
|
|
378
|
+
Free base fields only
|
|
379
|
+
Pro+ ``funding`` and ``details`` expansions
|
|
380
|
+
Business+ nested sub-tables (``cio_evaluation``, ``contracts``,
|
|
381
|
+
``projects``, ``cost_pools_towers``, ``funding_sources``,
|
|
382
|
+
``performance_metrics``, ``performance_actual``,
|
|
383
|
+
``operational_analysis``) and ``business_case_html``
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
uii: str
|
|
387
|
+
agency_code: int | None = None
|
|
388
|
+
agency_name: str | None = None
|
|
389
|
+
bureau_code: int | None = None
|
|
390
|
+
bureau_name: str | None = None
|
|
391
|
+
investment_title: str | None = None
|
|
392
|
+
type_of_investment: str | None = None
|
|
393
|
+
part_of_it_portfolio: str | None = None
|
|
394
|
+
updated_time: datetime | None = None
|
|
395
|
+
url: str | None = None
|
|
396
|
+
business_case_html: str | None = None
|
|
397
|
+
funding: dict[str, Any] | None = None
|
|
398
|
+
details: dict[str, Any] | None = None
|
|
399
|
+
cio_evaluation: list[dict[str, Any]] | None = None
|
|
400
|
+
contracts: list[dict[str, Any]] | None = None
|
|
401
|
+
projects: list[dict[str, Any]] | None = None
|
|
402
|
+
cost_pools_towers: list[dict[str, Any]] | None = None
|
|
403
|
+
funding_sources: list[dict[str, Any]] | None = None
|
|
404
|
+
performance_metrics: list[dict[str, Any]] | None = None
|
|
405
|
+
performance_actual: list[dict[str, Any]] | None = None
|
|
406
|
+
operational_analysis: list[dict[str, Any]] | None = None
|
|
407
|
+
|
|
408
|
+
|
|
370
409
|
@dataclass
|
|
371
410
|
class Vehicle:
|
|
372
411
|
"""Schema definition for Vehicle (not used for instances)"""
|
|
@@ -374,6 +413,12 @@ class Vehicle:
|
|
|
374
413
|
uuid: str
|
|
375
414
|
solicitation_identifier: str
|
|
376
415
|
agency_id: str
|
|
416
|
+
is_synthetic_solicitation: bool | None = None
|
|
417
|
+
program_acronym: str | None = None
|
|
418
|
+
description: str | None = None
|
|
419
|
+
idv_count: int | None = None
|
|
420
|
+
total_obligated: Decimal | None = None
|
|
421
|
+
latest_award_date: date | None = None
|
|
377
422
|
solicitation_title: str | None = None
|
|
378
423
|
solicitation_date: date | None = None
|
|
379
424
|
award_date: date | None = None
|
|
@@ -381,6 +426,24 @@ class Vehicle:
|
|
|
381
426
|
fiscal_year: int | None = None
|
|
382
427
|
|
|
383
428
|
|
|
429
|
+
@dataclass
|
|
430
|
+
class VehicleMetrics:
|
|
431
|
+
"""Schema definition for the Vehicle `metrics(*)` expansion (not used for instances)"""
|
|
432
|
+
|
|
433
|
+
avg_offers_received: float | None = None
|
|
434
|
+
award_concentration_hhi: float | None = None
|
|
435
|
+
order_concentration_hhi: float | None = None
|
|
436
|
+
competed_rate: float | None = None
|
|
437
|
+
using_agency_count: int | None = None
|
|
438
|
+
avg_order_value: float | None = None
|
|
439
|
+
max_order_value: float | None = None
|
|
440
|
+
top_recipient_share: float | None = None
|
|
441
|
+
recent_obligations_24mo: float | None = None
|
|
442
|
+
recent_orders_24mo: int | None = None
|
|
443
|
+
days_since_last_order: int | None = None
|
|
444
|
+
obligation_to_ceiling_ratio: float | None = None
|
|
445
|
+
|
|
446
|
+
|
|
384
447
|
@dataclass
|
|
385
448
|
class Entity:
|
|
386
449
|
"""Schema definition for Entity (not used for instances)"""
|
|
@@ -651,21 +714,34 @@ class ShapeConfig:
|
|
|
651
714
|
|
|
652
715
|
# Default for list_vehicles()
|
|
653
716
|
VEHICLES_MINIMAL: Final = (
|
|
654
|
-
"uuid,solicitation_identifier,
|
|
655
|
-
"
|
|
717
|
+
"uuid,solicitation_identifier,is_synthetic_solicitation,program_acronym,"
|
|
718
|
+
"organization_id,organization,vehicle_type,description,"
|
|
719
|
+
"idv_count,awardee_count,order_count,total_obligated,"
|
|
720
|
+
"vehicle_obligations,vehicle_contracts_value,latest_award_date,"
|
|
721
|
+
"solicitation_title,solicitation_date"
|
|
656
722
|
)
|
|
657
723
|
|
|
658
724
|
# Default for get_vehicle()
|
|
659
725
|
VEHICLES_COMPREHENSIVE: Final = (
|
|
660
|
-
"uuid,solicitation_identifier,agency_id,
|
|
661
|
-
"
|
|
662
|
-
"
|
|
663
|
-
"
|
|
726
|
+
"uuid,solicitation_identifier,is_synthetic_solicitation,agency_id,program_acronym,"
|
|
727
|
+
"organization_id,organization(*),vehicle_type,who_can_use,"
|
|
728
|
+
"solicitation_title,solicitation_description,solicitation_date,opportunity_id,"
|
|
729
|
+
"naics_code,psc_code,set_aside,"
|
|
730
|
+
"fiscal_year,award_date,latest_award_date,last_date_to_order,"
|
|
731
|
+
"description,idv_count,awardee_count,order_count,total_obligated,"
|
|
732
|
+
"vehicle_obligations,vehicle_contracts_value,"
|
|
733
|
+
"type_of_idc,contract_type,metrics(*)"
|
|
664
734
|
)
|
|
665
735
|
|
|
666
736
|
# Default for list_vehicle_awardees()
|
|
667
737
|
VEHICLE_AWARDEES_MINIMAL: Final = "uuid,key,piid,award_date,title,order_count,idv_obligations,idv_contracts_value,recipient(display_name,uei)"
|
|
668
738
|
|
|
739
|
+
# Default for list_vehicle_orders()
|
|
740
|
+
VEHICLE_ORDERS_MINIMAL: Final = (
|
|
741
|
+
"key,piid,award_date,obligated,total_contract_value,description,"
|
|
742
|
+
"recipient(display_name,uei)"
|
|
743
|
+
)
|
|
744
|
+
|
|
669
745
|
# Default for list_organizations()
|
|
670
746
|
ORGANIZATIONS_MINIMAL: Final = "key,fh_key,name,level,type,short_name"
|
|
671
747
|
|
|
@@ -687,3 +763,18 @@ class ShapeConfig:
|
|
|
687
763
|
GSA_ELIBRARY_CONTRACTS_MINIMAL: Final = (
|
|
688
764
|
"uuid,contract_number,schedule,recipient(display_name,uei),idv(key,award_date)"
|
|
689
765
|
)
|
|
766
|
+
|
|
767
|
+
# Default for list_itdashboard_investments()
|
|
768
|
+
# Free-tier safe: matches the API's INVESTMENT_LIST_DEFAULT_SHAPE.
|
|
769
|
+
ITDASHBOARD_INVESTMENTS_MINIMAL: Final = (
|
|
770
|
+
"uii,agency_name,bureau_name,investment_title,"
|
|
771
|
+
"type_of_investment,part_of_it_portfolio,updated_time,url"
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
# Default for get_itdashboard_investment()
|
|
775
|
+
# Free-tier safe: matches the API's INVESTMENT_RETRIEVE_DEFAULT_SHAPE.
|
|
776
|
+
ITDASHBOARD_INVESTMENTS_COMPREHENSIVE: Final = (
|
|
777
|
+
"uii,agency_code,agency_name,bureau_code,bureau_name,"
|
|
778
|
+
"investment_title,type_of_investment,part_of_it_portfolio,"
|
|
779
|
+
"updated_time,url"
|
|
780
|
+
)
|