tango-python 1.18.1__py3-none-any.whl → 1.19.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 +1 -1
- tango/client.py +68 -7
- tango/exceptions.py +10 -4
- {tango_python-1.18.1.dist-info → tango_python-1.19.0.dist-info}/METADATA +1 -1
- {tango_python-1.18.1.dist-info → tango_python-1.19.0.dist-info}/RECORD +8 -8
- {tango_python-1.18.1.dist-info → tango_python-1.19.0.dist-info}/WHEEL +0 -0
- {tango_python-1.18.1.dist-info → tango_python-1.19.0.dist-info}/entry_points.txt +0 -0
- {tango_python-1.18.1.dist-info → tango_python-1.19.0.dist-info}/licenses/LICENSE +0 -0
tango/__init__.py
CHANGED
tango/client.py
CHANGED
|
@@ -100,6 +100,10 @@ def _error_reason(error_data: dict[str, Any]) -> str | None:
|
|
|
100
100
|
return None
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
# Funding-organization keys the metrics endpoints accept in `group_by`.
|
|
104
|
+
MetricsGroupBy = Literal["agency", "department"]
|
|
105
|
+
|
|
106
|
+
|
|
103
107
|
class TangoClient:
|
|
104
108
|
"""Tango API Client"""
|
|
105
109
|
|
|
@@ -5615,7 +5619,15 @@ class TangoClient:
|
|
|
5615
5619
|
params["flat_lists"] = "true"
|
|
5616
5620
|
return self._get(f"/api/psc/{code}/", params)
|
|
5617
5621
|
|
|
5618
|
-
def get_psc_metrics(
|
|
5622
|
+
def get_psc_metrics(
|
|
5623
|
+
self,
|
|
5624
|
+
code: str,
|
|
5625
|
+
months: int,
|
|
5626
|
+
period_grouping: str,
|
|
5627
|
+
group_by: MetricsGroupBy | list[MetricsGroupBy] | None = None,
|
|
5628
|
+
fiscal_year: bool = False,
|
|
5629
|
+
rolling: bool = False,
|
|
5630
|
+
) -> dict[str, Any]:
|
|
5619
5631
|
"""Get rolling PSC metrics (`/api/psc/{code}/metrics/{months}/{period_grouping}/`).
|
|
5620
5632
|
|
|
5621
5633
|
Args:
|
|
@@ -5623,10 +5635,32 @@ class TangoClient:
|
|
|
5623
5635
|
months: Window size in months (e.g. 6, 12, 24, 36).
|
|
5624
5636
|
period_grouping: ``"month"``, ``"quarter"``, or ``"year"`` (the
|
|
5625
5637
|
values the API accepts on the path segment).
|
|
5638
|
+
group_by: Split each period by the funding ``"agency"`` (an L2 short code such as ``"2100"``) or ``"department"`` (the CGAC code as an int, e.g. ``97`` for DoD), or both.
|
|
5639
|
+
fiscal_year: Bucket by federal fiscal year (October-September) instead of calendar periods.
|
|
5640
|
+
rolling: Add rolling averages. The API skips them, with a ``warning``, when ``group_by`` is set.
|
|
5626
5641
|
"""
|
|
5627
5642
|
if not code:
|
|
5628
5643
|
raise TangoValidationError("PSC code is required")
|
|
5629
|
-
return self._get(
|
|
5644
|
+
return self._get(
|
|
5645
|
+
f"/api/psc/{code}/metrics/{months}/{period_grouping}/",
|
|
5646
|
+
self._metrics_params(group_by, fiscal_year, rolling),
|
|
5647
|
+
)
|
|
5648
|
+
|
|
5649
|
+
@staticmethod
|
|
5650
|
+
def _metrics_params(
|
|
5651
|
+
group_by: MetricsGroupBy | list[MetricsGroupBy] | None,
|
|
5652
|
+
fiscal_year: bool,
|
|
5653
|
+
rolling: bool,
|
|
5654
|
+
) -> dict[str, Any]:
|
|
5655
|
+
"""Query params shared by the entity, NAICS and PSC metrics endpoints."""
|
|
5656
|
+
params: dict[str, Any] = {}
|
|
5657
|
+
if group_by:
|
|
5658
|
+
params["group_by"] = group_by if isinstance(group_by, str) else ",".join(group_by)
|
|
5659
|
+
if fiscal_year:
|
|
5660
|
+
params["fiscal_year"] = "true"
|
|
5661
|
+
if rolling:
|
|
5662
|
+
params["rolling"] = "true"
|
|
5663
|
+
return params
|
|
5630
5664
|
|
|
5631
5665
|
def get_naics(
|
|
5632
5666
|
self,
|
|
@@ -5647,11 +5681,25 @@ class TangoClient:
|
|
|
5647
5681
|
params["flat_lists"] = "true"
|
|
5648
5682
|
return self._get(f"/api/naics/{code}/", params)
|
|
5649
5683
|
|
|
5650
|
-
def get_naics_metrics(
|
|
5651
|
-
|
|
5684
|
+
def get_naics_metrics(
|
|
5685
|
+
self,
|
|
5686
|
+
code: str,
|
|
5687
|
+
months: int,
|
|
5688
|
+
period_grouping: str,
|
|
5689
|
+
group_by: MetricsGroupBy | list[MetricsGroupBy] | None = None,
|
|
5690
|
+
fiscal_year: bool = False,
|
|
5691
|
+
rolling: bool = False,
|
|
5692
|
+
) -> dict[str, Any]:
|
|
5693
|
+
"""Get rolling NAICS metrics (`/api/naics/{code}/metrics/{months}/{period_grouping}/`).
|
|
5694
|
+
|
|
5695
|
+
Takes the same ``group_by``, ``fiscal_year`` and ``rolling`` options as :meth:`get_psc_metrics`.
|
|
5696
|
+
"""
|
|
5652
5697
|
if not code:
|
|
5653
5698
|
raise TangoValidationError("NAICS code is required")
|
|
5654
|
-
return self._get(
|
|
5699
|
+
return self._get(
|
|
5700
|
+
f"/api/naics/{code}/metrics/{months}/{period_grouping}/",
|
|
5701
|
+
self._metrics_params(group_by, fiscal_year, rolling),
|
|
5702
|
+
)
|
|
5655
5703
|
|
|
5656
5704
|
def get_business_type(
|
|
5657
5705
|
self,
|
|
@@ -6026,13 +6074,26 @@ class TangoClient:
|
|
|
6026
6074
|
meta=data.get("meta"),
|
|
6027
6075
|
)
|
|
6028
6076
|
|
|
6029
|
-
def get_entity_metrics(
|
|
6077
|
+
def get_entity_metrics(
|
|
6078
|
+
self,
|
|
6079
|
+
uei: str,
|
|
6080
|
+
months: int,
|
|
6081
|
+
period_grouping: str,
|
|
6082
|
+
group_by: MetricsGroupBy | list[MetricsGroupBy] | None = None,
|
|
6083
|
+
fiscal_year: bool = False,
|
|
6084
|
+
rolling: bool = False,
|
|
6085
|
+
) -> dict[str, Any]:
|
|
6030
6086
|
"""Get rolling metrics for an entity
|
|
6031
6087
|
(`/api/entities/{uei}/metrics/{months}/{period_grouping}/`).
|
|
6088
|
+
|
|
6089
|
+
Takes the same ``group_by``, ``fiscal_year`` and ``rolling`` options as :meth:`get_psc_metrics`.
|
|
6032
6090
|
"""
|
|
6033
6091
|
if not uei:
|
|
6034
6092
|
raise TangoValidationError("UEI is required")
|
|
6035
|
-
return self._get(
|
|
6093
|
+
return self._get(
|
|
6094
|
+
f"/api/entities/{uei}/metrics/{months}/{period_grouping}/",
|
|
6095
|
+
self._metrics_params(group_by, fiscal_year, rolling),
|
|
6096
|
+
)
|
|
6036
6097
|
|
|
6037
6098
|
# ============================================================================
|
|
6038
6099
|
# IDV sub-resources
|
tango/exceptions.py
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"""Tango API exceptions"""
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
6
|
+
# The API names the tripped rate in its 429 detail: "Rate limit exceeded for daily. Please try again in 16 hours, 58 minutes."
|
|
7
|
+
_RATE_NAME = re.compile(r"exceeded for ([\w-]+)", re.IGNORECASE)
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class TangoAPIError(Exception):
|
|
7
11
|
"""Base exception for Tango API errors"""
|
|
@@ -88,11 +92,13 @@ class TangoRateLimitError(TangoAPIError):
|
|
|
88
92
|
d = self.detail
|
|
89
93
|
if not d:
|
|
90
94
|
return None
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if "daily" in
|
|
95
|
+
# Read the rate's name, not the whole sentence: a daily lockout's wait reads "16 hours, 58 minutes", which a match on "minute" took for burst.
|
|
96
|
+
match = _RATE_NAME.search(d)
|
|
97
|
+
text = match.group(1).lower() if match else d.lower()
|
|
98
|
+
if "daily" in text or re.search(r"\bday\b", text):
|
|
95
99
|
return "daily"
|
|
100
|
+
if "burst" in text or "minute" in text:
|
|
101
|
+
return "burst"
|
|
96
102
|
return None
|
|
97
103
|
|
|
98
104
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
tango/__init__.py,sha256=
|
|
2
|
-
tango/client.py,sha256=
|
|
3
|
-
tango/exceptions.py,sha256=
|
|
1
|
+
tango/__init__.py,sha256=EM6Jq21IN9wUKCXR7DsHJFuseDT8AqktTOS59qaXm9w,2739
|
|
2
|
+
tango/client.py,sha256=2zQb486CL9sClQOJlua1nI_AnRFT1zUjeEikv_t_Cc4,247811
|
|
3
|
+
tango/exceptions.py,sha256=TL2MrSbcy99ogrJugLiP1I5J5qNih770knISycDTx-g,4661
|
|
4
4
|
tango/models.py,sha256=D9e8_AAAwVi1C4nOqdhWz-8ARBLPRsLGi8TDjucOMEY,72897
|
|
5
5
|
tango/shapes/__init__.py,sha256=7ea1WU74jp4znhNw-gXruag6m6eyPZtbVgbDFmFUWro,1072
|
|
6
6
|
tango/shapes/explicit_schemas.py,sha256=fwjHiFLOMpFyNLNM4M6kdK603s-t8gxHYqcSeL3jz7g,85674
|
|
@@ -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.
|
|
20
|
-
tango_python-1.
|
|
21
|
-
tango_python-1.
|
|
22
|
-
tango_python-1.
|
|
23
|
-
tango_python-1.
|
|
19
|
+
tango_python-1.19.0.dist-info/METADATA,sha256=IBq0ckYVquUbejqTc115sets4upLdf9aZOIKtJWEYBo,22824
|
|
20
|
+
tango_python-1.19.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
21
|
+
tango_python-1.19.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
|
|
22
|
+
tango_python-1.19.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
|
|
23
|
+
tango_python-1.19.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|