tango-python 0.3.0__py3-none-any.whl → 0.4.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 +453 -16
- tango/models.py +61 -0
- tango/shapes/explicit_schemas.py +75 -0
- {tango_python-0.3.0.dist-info → tango_python-0.4.0.dist-info}/METADATA +13 -6
- {tango_python-0.3.0.dist-info → tango_python-0.4.0.dist-info}/RECORD +8 -8
- {tango_python-0.3.0.dist-info → tango_python-0.4.0.dist-info}/WHEEL +1 -1
- {tango_python-0.3.0.dist-info → tango_python-0.4.0.dist-info}/licenses/LICENSE +0 -0
tango/__init__.py
CHANGED
tango/client.py
CHANGED
|
@@ -17,6 +17,8 @@ from tango.exceptions import (
|
|
|
17
17
|
)
|
|
18
18
|
from tango.models import (
|
|
19
19
|
IDV,
|
|
20
|
+
OTA,
|
|
21
|
+
OTIDV,
|
|
20
22
|
Agency,
|
|
21
23
|
BusinessType,
|
|
22
24
|
Contract,
|
|
@@ -26,9 +28,11 @@ from tango.models import (
|
|
|
26
28
|
Location,
|
|
27
29
|
Notice,
|
|
28
30
|
Opportunity,
|
|
31
|
+
Organization,
|
|
29
32
|
PaginatedResponse,
|
|
30
33
|
SearchFilters,
|
|
31
34
|
ShapeConfig,
|
|
35
|
+
Subaward,
|
|
32
36
|
Vehicle,
|
|
33
37
|
WebhookEndpoint,
|
|
34
38
|
WebhookEventType,
|
|
@@ -362,9 +366,13 @@ class TangoClient:
|
|
|
362
366
|
# ============================================================================
|
|
363
367
|
|
|
364
368
|
# Agency endpoints
|
|
365
|
-
def list_agencies(
|
|
369
|
+
def list_agencies(
|
|
370
|
+
self, page: int = 1, limit: int = 25, search: str | None = None
|
|
371
|
+
) -> PaginatedResponse:
|
|
366
372
|
"""List all agencies"""
|
|
367
|
-
params = {"page": page, "limit": min(limit, 100)}
|
|
373
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
374
|
+
if search:
|
|
375
|
+
params["search"] = search
|
|
368
376
|
data = self._get("/api/agencies/", params)
|
|
369
377
|
return PaginatedResponse(
|
|
370
378
|
count=data["count"],
|
|
@@ -389,6 +397,96 @@ class TangoClient:
|
|
|
389
397
|
raise TangoNotFoundError(f"Agency '{code}' not found", 404)
|
|
390
398
|
return agency
|
|
391
399
|
|
|
400
|
+
def list_offices(
|
|
401
|
+
self,
|
|
402
|
+
page: int = 1,
|
|
403
|
+
limit: int = 25,
|
|
404
|
+
search: str | None = None,
|
|
405
|
+
) -> PaginatedResponse:
|
|
406
|
+
"""List offices (`/api/offices/`)."""
|
|
407
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
408
|
+
if search is not None:
|
|
409
|
+
params["search"] = search
|
|
410
|
+
data = self._get("/api/offices/", params)
|
|
411
|
+
return PaginatedResponse(
|
|
412
|
+
count=data.get("count", 0),
|
|
413
|
+
next=data.get("next"),
|
|
414
|
+
previous=data.get("previous"),
|
|
415
|
+
results=data.get("results", []),
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
def get_office(self, code: str) -> dict[str, Any]:
|
|
419
|
+
"""Get a single office by code (`/api/offices/{code}/`)."""
|
|
420
|
+
return self._get(f"/api/offices/{code}/")
|
|
421
|
+
|
|
422
|
+
def list_organizations(
|
|
423
|
+
self,
|
|
424
|
+
page: int = 1,
|
|
425
|
+
limit: int = 25,
|
|
426
|
+
shape: str | None = None,
|
|
427
|
+
flat: bool = False,
|
|
428
|
+
flat_lists: bool = False,
|
|
429
|
+
cgac: str | None = None,
|
|
430
|
+
include_inactive: bool | None = None,
|
|
431
|
+
level: int | None = None,
|
|
432
|
+
parent: str | None = None,
|
|
433
|
+
search: str | None = None,
|
|
434
|
+
type: str | None = None,
|
|
435
|
+
) -> PaginatedResponse:
|
|
436
|
+
"""List organizations (`/api/organizations/`)."""
|
|
437
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
438
|
+
if shape is None:
|
|
439
|
+
shape = ShapeConfig.ORGANIZATIONS_MINIMAL
|
|
440
|
+
if shape:
|
|
441
|
+
params["shape"] = shape
|
|
442
|
+
if flat:
|
|
443
|
+
params["flat"] = "true"
|
|
444
|
+
if flat_lists:
|
|
445
|
+
params["flat_lists"] = "true"
|
|
446
|
+
if cgac is not None:
|
|
447
|
+
params["cgac"] = cgac
|
|
448
|
+
if include_inactive is not None:
|
|
449
|
+
params["include_inactive"] = include_inactive
|
|
450
|
+
if level is not None:
|
|
451
|
+
params["level"] = level
|
|
452
|
+
if parent is not None:
|
|
453
|
+
params["parent"] = parent
|
|
454
|
+
if search is not None:
|
|
455
|
+
params["search"] = search
|
|
456
|
+
if type is not None:
|
|
457
|
+
params["type"] = type
|
|
458
|
+
data = self._get("/api/organizations/", params)
|
|
459
|
+
results = [
|
|
460
|
+
self._parse_response_with_shape(obj, shape, Organization, flat, flat_lists)
|
|
461
|
+
for obj in data.get("results", [])
|
|
462
|
+
]
|
|
463
|
+
return PaginatedResponse(
|
|
464
|
+
count=data.get("count", 0),
|
|
465
|
+
next=data.get("next"),
|
|
466
|
+
previous=data.get("previous"),
|
|
467
|
+
results=results,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
def get_organization(
|
|
471
|
+
self,
|
|
472
|
+
fh_key: str,
|
|
473
|
+
shape: str | None = None,
|
|
474
|
+
flat: bool = False,
|
|
475
|
+
flat_lists: bool = False,
|
|
476
|
+
) -> Any:
|
|
477
|
+
"""Get a single organization by fh_key (`/api/organizations/{fh_key}/`)."""
|
|
478
|
+
params: dict[str, Any] = {}
|
|
479
|
+
if shape is None:
|
|
480
|
+
shape = ShapeConfig.ORGANIZATIONS_MINIMAL
|
|
481
|
+
if shape:
|
|
482
|
+
params["shape"] = shape
|
|
483
|
+
if flat:
|
|
484
|
+
params["flat"] = "true"
|
|
485
|
+
if flat_lists:
|
|
486
|
+
params["flat_lists"] = "true"
|
|
487
|
+
data = self._get(f"/api/organizations/{fh_key}/", params)
|
|
488
|
+
return self._parse_response_with_shape(data, shape, Organization, flat, flat_lists)
|
|
489
|
+
|
|
392
490
|
# Contract endpoints
|
|
393
491
|
def list_contracts(
|
|
394
492
|
self,
|
|
@@ -398,7 +496,7 @@ class TangoClient:
|
|
|
398
496
|
flat: bool = False,
|
|
399
497
|
flat_lists: bool = False,
|
|
400
498
|
filters: SearchFilters | dict[str, Any] | None = None,
|
|
401
|
-
**kwargs,
|
|
499
|
+
**kwargs: Any,
|
|
402
500
|
) -> PaginatedResponse:
|
|
403
501
|
"""
|
|
404
502
|
List contracts with optional filtering
|
|
@@ -600,7 +698,7 @@ class TangoClient:
|
|
|
600
698
|
flat: bool = False,
|
|
601
699
|
flat_lists: bool = False,
|
|
602
700
|
joiner: str = ".",
|
|
603
|
-
**filters,
|
|
701
|
+
**filters: Any,
|
|
604
702
|
) -> PaginatedResponse:
|
|
605
703
|
"""
|
|
606
704
|
List IDVs (indefinite delivery vehicles) with keyset pagination.
|
|
@@ -675,7 +773,7 @@ class TangoClient:
|
|
|
675
773
|
flat_lists: bool = False,
|
|
676
774
|
joiner: str = ".",
|
|
677
775
|
filters: SearchFilters | dict[str, Any] | None = None,
|
|
678
|
-
**kwargs,
|
|
776
|
+
**kwargs: Any,
|
|
679
777
|
) -> PaginatedResponse:
|
|
680
778
|
"""
|
|
681
779
|
List child awards (contracts) under an IDV (`/api/idvs/{key}/awards/`).
|
|
@@ -751,7 +849,7 @@ class TangoClient:
|
|
|
751
849
|
flat: bool = False,
|
|
752
850
|
flat_lists: bool = False,
|
|
753
851
|
joiner: str = ".",
|
|
754
|
-
**filters,
|
|
852
|
+
**filters: Any,
|
|
755
853
|
) -> PaginatedResponse:
|
|
756
854
|
"""List child IDVs under an IDV (`/api/idvs/{key}/idvs/`)."""
|
|
757
855
|
params: dict[str, Any] = {"limit": min(limit, 100)}
|
|
@@ -828,6 +926,268 @@ class TangoClient:
|
|
|
828
926
|
page_metadata=data.get("page_metadata"),
|
|
829
927
|
)
|
|
830
928
|
|
|
929
|
+
def list_otas(
|
|
930
|
+
self,
|
|
931
|
+
limit: int = 25,
|
|
932
|
+
cursor: str | None = None,
|
|
933
|
+
shape: str | None = None,
|
|
934
|
+
flat: bool = False,
|
|
935
|
+
flat_lists: bool = False,
|
|
936
|
+
joiner: str = ".",
|
|
937
|
+
award_date: str | None = None,
|
|
938
|
+
award_date_gte: str | None = None,
|
|
939
|
+
award_date_lte: str | None = None,
|
|
940
|
+
awarding_agency: str | None = None,
|
|
941
|
+
expiring_gte: str | None = None,
|
|
942
|
+
expiring_lte: str | None = None,
|
|
943
|
+
fiscal_year: int | None = None,
|
|
944
|
+
fiscal_year_gte: int | None = None,
|
|
945
|
+
fiscal_year_lte: int | None = None,
|
|
946
|
+
funding_agency: str | None = None,
|
|
947
|
+
ordering: str | None = None,
|
|
948
|
+
piid: str | None = None,
|
|
949
|
+
pop_end_date_gte: str | None = None,
|
|
950
|
+
pop_end_date_lte: str | None = None,
|
|
951
|
+
pop_start_date_gte: str | None = None,
|
|
952
|
+
pop_start_date_lte: str | None = None,
|
|
953
|
+
psc: str | None = None,
|
|
954
|
+
recipient: str | None = None,
|
|
955
|
+
search: str | None = None,
|
|
956
|
+
uei: str | None = None,
|
|
957
|
+
) -> PaginatedResponse:
|
|
958
|
+
"""List OTAs (Other Transaction Agreements) (`/api/otas/`). Keyset pagination."""
|
|
959
|
+
params: dict[str, Any] = {"limit": min(limit, 100)}
|
|
960
|
+
if cursor:
|
|
961
|
+
params["cursor"] = cursor
|
|
962
|
+
if shape is None:
|
|
963
|
+
shape = ShapeConfig.OTAS_MINIMAL
|
|
964
|
+
if shape:
|
|
965
|
+
params["shape"] = shape
|
|
966
|
+
if flat:
|
|
967
|
+
params["flat"] = "true"
|
|
968
|
+
if joiner:
|
|
969
|
+
params["joiner"] = joiner
|
|
970
|
+
if flat_lists:
|
|
971
|
+
params["flat_lists"] = "true"
|
|
972
|
+
for key, val in (
|
|
973
|
+
("award_date", award_date),
|
|
974
|
+
("award_date_gte", award_date_gte),
|
|
975
|
+
("award_date_lte", award_date_lte),
|
|
976
|
+
("awarding_agency", awarding_agency),
|
|
977
|
+
("expiring_gte", expiring_gte),
|
|
978
|
+
("expiring_lte", expiring_lte),
|
|
979
|
+
("fiscal_year", fiscal_year),
|
|
980
|
+
("fiscal_year_gte", fiscal_year_gte),
|
|
981
|
+
("fiscal_year_lte", fiscal_year_lte),
|
|
982
|
+
("funding_agency", funding_agency),
|
|
983
|
+
("ordering", ordering),
|
|
984
|
+
("piid", piid),
|
|
985
|
+
("pop_end_date_gte", pop_end_date_gte),
|
|
986
|
+
("pop_end_date_lte", pop_end_date_lte),
|
|
987
|
+
("pop_start_date_gte", pop_start_date_gte),
|
|
988
|
+
("pop_start_date_lte", pop_start_date_lte),
|
|
989
|
+
("psc", psc),
|
|
990
|
+
("recipient", recipient),
|
|
991
|
+
("search", search),
|
|
992
|
+
("uei", uei),
|
|
993
|
+
):
|
|
994
|
+
if val is not None:
|
|
995
|
+
params[key] = val
|
|
996
|
+
data = self._get("/api/otas/", params)
|
|
997
|
+
raw_results = data.get("results") or []
|
|
998
|
+
results = [
|
|
999
|
+
self._parse_response_with_shape(obj, shape, OTA, flat, flat_lists, joiner=joiner)
|
|
1000
|
+
for obj in raw_results
|
|
1001
|
+
]
|
|
1002
|
+
return PaginatedResponse(
|
|
1003
|
+
count=int(data.get("count") or len(results)),
|
|
1004
|
+
next=data.get("next"),
|
|
1005
|
+
previous=data.get("previous"),
|
|
1006
|
+
results=results,
|
|
1007
|
+
cursor=data.get("cursor"),
|
|
1008
|
+
page_metadata=data.get("page_metadata"),
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
def get_ota(
|
|
1012
|
+
self,
|
|
1013
|
+
key: str,
|
|
1014
|
+
shape: str | None = None,
|
|
1015
|
+
flat: bool = False,
|
|
1016
|
+
flat_lists: bool = False,
|
|
1017
|
+
joiner: str = ".",
|
|
1018
|
+
) -> Any:
|
|
1019
|
+
"""Get a single OTA by key (`/api/otas/{key}/`)."""
|
|
1020
|
+
params: dict[str, Any] = {}
|
|
1021
|
+
if shape is None:
|
|
1022
|
+
shape = ShapeConfig.OTAS_MINIMAL
|
|
1023
|
+
if shape:
|
|
1024
|
+
params["shape"] = shape
|
|
1025
|
+
if flat:
|
|
1026
|
+
params["flat"] = "true"
|
|
1027
|
+
if joiner:
|
|
1028
|
+
params["joiner"] = joiner
|
|
1029
|
+
if flat_lists:
|
|
1030
|
+
params["flat_lists"] = "true"
|
|
1031
|
+
data = self._get(f"/api/otas/{key}/", params)
|
|
1032
|
+
return self._parse_response_with_shape(data, shape, OTA, flat, flat_lists, joiner=joiner)
|
|
1033
|
+
|
|
1034
|
+
def list_otidvs(
|
|
1035
|
+
self,
|
|
1036
|
+
limit: int = 25,
|
|
1037
|
+
cursor: str | None = None,
|
|
1038
|
+
shape: str | None = None,
|
|
1039
|
+
flat: bool = False,
|
|
1040
|
+
flat_lists: bool = False,
|
|
1041
|
+
joiner: str = ".",
|
|
1042
|
+
award_date: str | None = None,
|
|
1043
|
+
award_date_gte: str | None = None,
|
|
1044
|
+
award_date_lte: str | None = None,
|
|
1045
|
+
awarding_agency: str | None = None,
|
|
1046
|
+
expiring_gte: str | None = None,
|
|
1047
|
+
expiring_lte: str | None = None,
|
|
1048
|
+
fiscal_year: int | None = None,
|
|
1049
|
+
fiscal_year_gte: int | None = None,
|
|
1050
|
+
fiscal_year_lte: int | None = None,
|
|
1051
|
+
funding_agency: str | None = None,
|
|
1052
|
+
ordering: str | None = None,
|
|
1053
|
+
piid: str | None = None,
|
|
1054
|
+
pop_end_date_gte: str | None = None,
|
|
1055
|
+
pop_end_date_lte: str | None = None,
|
|
1056
|
+
pop_start_date_gte: str | None = None,
|
|
1057
|
+
pop_start_date_lte: str | None = None,
|
|
1058
|
+
psc: str | None = None,
|
|
1059
|
+
recipient: str | None = None,
|
|
1060
|
+
search: str | None = None,
|
|
1061
|
+
uei: str | None = None,
|
|
1062
|
+
) -> PaginatedResponse:
|
|
1063
|
+
"""List OTIDVs (Other Transaction IDVs) (`/api/otidvs/`). Keyset pagination."""
|
|
1064
|
+
params: dict[str, Any] = {"limit": min(limit, 100)}
|
|
1065
|
+
if cursor:
|
|
1066
|
+
params["cursor"] = cursor
|
|
1067
|
+
if shape is None:
|
|
1068
|
+
shape = ShapeConfig.OTIDVS_MINIMAL
|
|
1069
|
+
if shape:
|
|
1070
|
+
params["shape"] = shape
|
|
1071
|
+
if flat:
|
|
1072
|
+
params["flat"] = "true"
|
|
1073
|
+
if joiner:
|
|
1074
|
+
params["joiner"] = joiner
|
|
1075
|
+
if flat_lists:
|
|
1076
|
+
params["flat_lists"] = "true"
|
|
1077
|
+
for key, val in (
|
|
1078
|
+
("award_date", award_date),
|
|
1079
|
+
("award_date_gte", award_date_gte),
|
|
1080
|
+
("award_date_lte", award_date_lte),
|
|
1081
|
+
("awarding_agency", awarding_agency),
|
|
1082
|
+
("expiring_gte", expiring_gte),
|
|
1083
|
+
("expiring_lte", expiring_lte),
|
|
1084
|
+
("fiscal_year", fiscal_year),
|
|
1085
|
+
("fiscal_year_gte", fiscal_year_gte),
|
|
1086
|
+
("fiscal_year_lte", fiscal_year_lte),
|
|
1087
|
+
("funding_agency", funding_agency),
|
|
1088
|
+
("ordering", ordering),
|
|
1089
|
+
("piid", piid),
|
|
1090
|
+
("pop_end_date_gte", pop_end_date_gte),
|
|
1091
|
+
("pop_end_date_lte", pop_end_date_lte),
|
|
1092
|
+
("pop_start_date_gte", pop_start_date_gte),
|
|
1093
|
+
("pop_start_date_lte", pop_start_date_lte),
|
|
1094
|
+
("psc", psc),
|
|
1095
|
+
("recipient", recipient),
|
|
1096
|
+
("search", search),
|
|
1097
|
+
("uei", uei),
|
|
1098
|
+
):
|
|
1099
|
+
if val is not None:
|
|
1100
|
+
params[key] = val
|
|
1101
|
+
data = self._get("/api/otidvs/", params)
|
|
1102
|
+
raw_results = data.get("results") or []
|
|
1103
|
+
results = [
|
|
1104
|
+
self._parse_response_with_shape(obj, shape, OTIDV, flat, flat_lists, joiner=joiner)
|
|
1105
|
+
for obj in raw_results
|
|
1106
|
+
]
|
|
1107
|
+
return PaginatedResponse(
|
|
1108
|
+
count=int(data.get("count") or len(results)),
|
|
1109
|
+
next=data.get("next"),
|
|
1110
|
+
previous=data.get("previous"),
|
|
1111
|
+
results=results,
|
|
1112
|
+
cursor=data.get("cursor"),
|
|
1113
|
+
page_metadata=data.get("page_metadata"),
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
def get_otidv(
|
|
1117
|
+
self,
|
|
1118
|
+
key: str,
|
|
1119
|
+
shape: str | None = None,
|
|
1120
|
+
flat: bool = False,
|
|
1121
|
+
flat_lists: bool = False,
|
|
1122
|
+
joiner: str = ".",
|
|
1123
|
+
) -> Any:
|
|
1124
|
+
"""Get a single OTIDV by key (`/api/otidvs/{key}/`)."""
|
|
1125
|
+
params: dict[str, Any] = {}
|
|
1126
|
+
if shape is None:
|
|
1127
|
+
shape = ShapeConfig.OTIDVS_MINIMAL
|
|
1128
|
+
if shape:
|
|
1129
|
+
params["shape"] = shape
|
|
1130
|
+
if flat:
|
|
1131
|
+
params["flat"] = "true"
|
|
1132
|
+
if joiner:
|
|
1133
|
+
params["joiner"] = joiner
|
|
1134
|
+
if flat_lists:
|
|
1135
|
+
params["flat_lists"] = "true"
|
|
1136
|
+
data = self._get(f"/api/otidvs/{key}/", params)
|
|
1137
|
+
return self._parse_response_with_shape(data, shape, OTIDV, flat, flat_lists, joiner=joiner)
|
|
1138
|
+
|
|
1139
|
+
def list_subawards(
|
|
1140
|
+
self,
|
|
1141
|
+
page: int = 1,
|
|
1142
|
+
limit: int = 25,
|
|
1143
|
+
shape: str | None = None,
|
|
1144
|
+
flat: bool = False,
|
|
1145
|
+
flat_lists: bool = False,
|
|
1146
|
+
award_key: str | None = None,
|
|
1147
|
+
awarding_agency: str | None = None,
|
|
1148
|
+
fiscal_year: int | None = None,
|
|
1149
|
+
fiscal_year_gte: int | None = None,
|
|
1150
|
+
fiscal_year_lte: int | None = None,
|
|
1151
|
+
funding_agency: str | None = None,
|
|
1152
|
+
prime_uei: str | None = None,
|
|
1153
|
+
recipient: str | None = None,
|
|
1154
|
+
sub_uei: str | None = None,
|
|
1155
|
+
) -> PaginatedResponse:
|
|
1156
|
+
"""List subawards (`/api/subawards/`)."""
|
|
1157
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1158
|
+
if shape is None:
|
|
1159
|
+
shape = ShapeConfig.SUBAWARDS_MINIMAL
|
|
1160
|
+
if shape:
|
|
1161
|
+
params["shape"] = shape
|
|
1162
|
+
if flat:
|
|
1163
|
+
params["flat"] = "true"
|
|
1164
|
+
if flat_lists:
|
|
1165
|
+
params["flat_lists"] = "true"
|
|
1166
|
+
for key, val in (
|
|
1167
|
+
("award_key", award_key),
|
|
1168
|
+
("awarding_agency", awarding_agency),
|
|
1169
|
+
("fiscal_year", fiscal_year),
|
|
1170
|
+
("fiscal_year_gte", fiscal_year_gte),
|
|
1171
|
+
("fiscal_year_lte", fiscal_year_lte),
|
|
1172
|
+
("funding_agency", funding_agency),
|
|
1173
|
+
("prime_uei", prime_uei),
|
|
1174
|
+
("recipient", recipient),
|
|
1175
|
+
("sub_uei", sub_uei),
|
|
1176
|
+
):
|
|
1177
|
+
if val is not None:
|
|
1178
|
+
params[key] = val
|
|
1179
|
+
data = self._get("/api/subawards/", params)
|
|
1180
|
+
results = [
|
|
1181
|
+
self._parse_response_with_shape(obj, shape, Subaward, flat, flat_lists)
|
|
1182
|
+
for obj in data.get("results", [])
|
|
1183
|
+
]
|
|
1184
|
+
return PaginatedResponse(
|
|
1185
|
+
count=data.get("count", 0),
|
|
1186
|
+
next=data.get("next"),
|
|
1187
|
+
previous=data.get("previous"),
|
|
1188
|
+
results=results,
|
|
1189
|
+
)
|
|
1190
|
+
|
|
831
1191
|
# ============================================================================
|
|
832
1192
|
# Vehicles (Awards)
|
|
833
1193
|
# ============================================================================
|
|
@@ -957,6 +1317,42 @@ class TangoClient:
|
|
|
957
1317
|
results=[BusinessType(**btype) for btype in data["results"]],
|
|
958
1318
|
)
|
|
959
1319
|
|
|
1320
|
+
def list_naics(
|
|
1321
|
+
self,
|
|
1322
|
+
page: int = 1,
|
|
1323
|
+
limit: int = 25,
|
|
1324
|
+
employee_limit: int | None = None,
|
|
1325
|
+
employee_limit_gte: int | None = None,
|
|
1326
|
+
employee_limit_lte: int | None = None,
|
|
1327
|
+
revenue_limit: int | None = None,
|
|
1328
|
+
revenue_limit_gte: int | None = None,
|
|
1329
|
+
revenue_limit_lte: int | None = None,
|
|
1330
|
+
search: str | None = None,
|
|
1331
|
+
) -> PaginatedResponse:
|
|
1332
|
+
"""List NAICS codes (`/api/naics/`)."""
|
|
1333
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1334
|
+
if employee_limit is not None:
|
|
1335
|
+
params["employee_limit"] = employee_limit
|
|
1336
|
+
if employee_limit_gte is not None:
|
|
1337
|
+
params["employee_limit_gte"] = employee_limit_gte
|
|
1338
|
+
if employee_limit_lte is not None:
|
|
1339
|
+
params["employee_limit_lte"] = employee_limit_lte
|
|
1340
|
+
if revenue_limit is not None:
|
|
1341
|
+
params["revenue_limit"] = revenue_limit
|
|
1342
|
+
if revenue_limit_gte is not None:
|
|
1343
|
+
params["revenue_limit_gte"] = revenue_limit_gte
|
|
1344
|
+
if revenue_limit_lte is not None:
|
|
1345
|
+
params["revenue_limit_lte"] = revenue_limit_lte
|
|
1346
|
+
if search is not None:
|
|
1347
|
+
params["search"] = search
|
|
1348
|
+
data = self._get("/api/naics/", params)
|
|
1349
|
+
return PaginatedResponse(
|
|
1350
|
+
count=data.get("count", 0),
|
|
1351
|
+
next=data.get("next"),
|
|
1352
|
+
previous=data.get("previous"),
|
|
1353
|
+
results=data.get("results", []),
|
|
1354
|
+
)
|
|
1355
|
+
|
|
960
1356
|
# Entity endpoints
|
|
961
1357
|
def list_entities(
|
|
962
1358
|
self,
|
|
@@ -966,7 +1362,7 @@ class TangoClient:
|
|
|
966
1362
|
flat: bool = False,
|
|
967
1363
|
flat_lists: bool = False,
|
|
968
1364
|
search: str | None = None,
|
|
969
|
-
**filters,
|
|
1365
|
+
**filters: Any,
|
|
970
1366
|
) -> PaginatedResponse:
|
|
971
1367
|
"""
|
|
972
1368
|
List entities (vendors/recipients)
|
|
@@ -980,7 +1376,7 @@ class TangoClient:
|
|
|
980
1376
|
search: Search query (maps to 'q' parameter)
|
|
981
1377
|
**filters: Additional filter parameters (uei, cage_code, etc.)
|
|
982
1378
|
"""
|
|
983
|
-
params = {"page": page, "limit": min(limit, 100)}
|
|
1379
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
984
1380
|
|
|
985
1381
|
# Add shape parameter with default minimal shape
|
|
986
1382
|
if shape is None:
|
|
@@ -1046,7 +1442,7 @@ class TangoClient:
|
|
|
1046
1442
|
shape: str | None = None,
|
|
1047
1443
|
flat: bool = False,
|
|
1048
1444
|
flat_lists: bool = False,
|
|
1049
|
-
**filters,
|
|
1445
|
+
**filters: Any,
|
|
1050
1446
|
) -> PaginatedResponse:
|
|
1051
1447
|
"""
|
|
1052
1448
|
List contract forecasts
|
|
@@ -1059,7 +1455,7 @@ class TangoClient:
|
|
|
1059
1455
|
flat_lists: If True, flatten arrays using indexed keys
|
|
1060
1456
|
**filters: Additional filter parameters
|
|
1061
1457
|
"""
|
|
1062
|
-
params = {"page": page, "limit": min(limit, 100)}
|
|
1458
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1063
1459
|
|
|
1064
1460
|
# Add shape parameter with default minimal shape
|
|
1065
1461
|
if shape is None:
|
|
@@ -1096,7 +1492,7 @@ class TangoClient:
|
|
|
1096
1492
|
shape: str | None = None,
|
|
1097
1493
|
flat: bool = False,
|
|
1098
1494
|
flat_lists: bool = False,
|
|
1099
|
-
**filters,
|
|
1495
|
+
**filters: Any,
|
|
1100
1496
|
) -> PaginatedResponse:
|
|
1101
1497
|
"""
|
|
1102
1498
|
List contract opportunities/solicitations
|
|
@@ -1109,7 +1505,7 @@ class TangoClient:
|
|
|
1109
1505
|
flat_lists: If True, flatten arrays using indexed keys
|
|
1110
1506
|
**filters: Additional filter parameters
|
|
1111
1507
|
"""
|
|
1112
|
-
params = {"page": page, "limit": min(limit, 100)}
|
|
1508
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1113
1509
|
|
|
1114
1510
|
# Add shape parameter with default minimal shape
|
|
1115
1511
|
if shape is None:
|
|
@@ -1146,7 +1542,7 @@ class TangoClient:
|
|
|
1146
1542
|
shape: str | None = None,
|
|
1147
1543
|
flat: bool = False,
|
|
1148
1544
|
flat_lists: bool = False,
|
|
1149
|
-
**filters,
|
|
1545
|
+
**filters: Any,
|
|
1150
1546
|
) -> PaginatedResponse:
|
|
1151
1547
|
"""
|
|
1152
1548
|
List contract notices
|
|
@@ -1161,7 +1557,7 @@ class TangoClient:
|
|
|
1161
1557
|
flat_lists: If True, flatten arrays using indexed keys
|
|
1162
1558
|
**filters: Additional filter parameters
|
|
1163
1559
|
"""
|
|
1164
|
-
params = {"page": page, "limit": min(limit, 100)}
|
|
1560
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1165
1561
|
|
|
1166
1562
|
# Add shape parameter with default minimal shape
|
|
1167
1563
|
if shape is None:
|
|
@@ -1198,7 +1594,7 @@ class TangoClient:
|
|
|
1198
1594
|
shape: str | None = None,
|
|
1199
1595
|
flat: bool = False,
|
|
1200
1596
|
flat_lists: bool = False,
|
|
1201
|
-
**filters,
|
|
1597
|
+
**filters: Any,
|
|
1202
1598
|
) -> PaginatedResponse:
|
|
1203
1599
|
"""
|
|
1204
1600
|
List grants
|
|
@@ -1213,7 +1609,7 @@ class TangoClient:
|
|
|
1213
1609
|
flat_lists: If True, flatten arrays using indexed keys
|
|
1214
1610
|
**filters: Additional filter parameters
|
|
1215
1611
|
"""
|
|
1216
|
-
params = {"page": page, "limit": min(limit, 100)}
|
|
1612
|
+
params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
|
|
1217
1613
|
|
|
1218
1614
|
# Add shape parameter with default minimal shape
|
|
1219
1615
|
if shape is None:
|
|
@@ -1242,6 +1638,47 @@ class TangoClient:
|
|
|
1242
1638
|
results=results,
|
|
1243
1639
|
)
|
|
1244
1640
|
|
|
1641
|
+
def list_assistance(
|
|
1642
|
+
self,
|
|
1643
|
+
limit: int = 25,
|
|
1644
|
+
cursor: str | None = None,
|
|
1645
|
+
assistance_type: str | None = None,
|
|
1646
|
+
award_key: str | None = None,
|
|
1647
|
+
fiscal_year: int | None = None,
|
|
1648
|
+
fiscal_year_gte: int | None = None,
|
|
1649
|
+
fiscal_year_lte: int | None = None,
|
|
1650
|
+
highly_compensated_officers: str | None = None,
|
|
1651
|
+
recipient: str | None = None,
|
|
1652
|
+
recipient_address: str | None = None,
|
|
1653
|
+
search: str | None = None,
|
|
1654
|
+
) -> PaginatedResponse:
|
|
1655
|
+
"""List assistance (financial assistance) transactions (`/api/assistance/`). Keyset pagination."""
|
|
1656
|
+
params: dict[str, Any] = {"limit": min(limit, 100)}
|
|
1657
|
+
if cursor:
|
|
1658
|
+
params["cursor"] = cursor
|
|
1659
|
+
for key, val in (
|
|
1660
|
+
("assistance_type", assistance_type),
|
|
1661
|
+
("award_key", award_key),
|
|
1662
|
+
("fiscal_year", fiscal_year),
|
|
1663
|
+
("fiscal_year_gte", fiscal_year_gte),
|
|
1664
|
+
("fiscal_year_lte", fiscal_year_lte),
|
|
1665
|
+
("highly_compensated_officers", highly_compensated_officers),
|
|
1666
|
+
("recipient", recipient),
|
|
1667
|
+
("recipient_address", recipient_address),
|
|
1668
|
+
("search", search),
|
|
1669
|
+
):
|
|
1670
|
+
if val is not None:
|
|
1671
|
+
params[key] = val
|
|
1672
|
+
data = self._get("/api/assistance/", params)
|
|
1673
|
+
return PaginatedResponse(
|
|
1674
|
+
count=int(data.get("count") or len(data.get("results") or [])),
|
|
1675
|
+
next=data.get("next"),
|
|
1676
|
+
previous=data.get("previous"),
|
|
1677
|
+
results=data.get("results", []),
|
|
1678
|
+
cursor=data.get("cursor"),
|
|
1679
|
+
page_metadata=data.get("page_metadata"),
|
|
1680
|
+
)
|
|
1681
|
+
|
|
1245
1682
|
# ============================================================================
|
|
1246
1683
|
# Webhooks (v2)
|
|
1247
1684
|
# ============================================================================
|
tango/models.py
CHANGED
|
@@ -295,6 +295,50 @@ class IDV:
|
|
|
295
295
|
recipient: RecipientProfile | None = None
|
|
296
296
|
|
|
297
297
|
|
|
298
|
+
@dataclass
|
|
299
|
+
class Organization:
|
|
300
|
+
"""Schema definition for Organization (not used for instances)"""
|
|
301
|
+
|
|
302
|
+
key: str
|
|
303
|
+
fh_key: str | None = None
|
|
304
|
+
name: str | None = None
|
|
305
|
+
level: int | None = None
|
|
306
|
+
type: str | None = None
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@dataclass
|
|
310
|
+
class OTA:
|
|
311
|
+
"""Schema definition for OTA / Other Transaction Agreement (not used for instances)"""
|
|
312
|
+
|
|
313
|
+
key: str
|
|
314
|
+
piid: str | None = None
|
|
315
|
+
award_date: date | None = None
|
|
316
|
+
description: str | None = None
|
|
317
|
+
recipient: RecipientProfile | None = None
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@dataclass
|
|
321
|
+
class OTIDV:
|
|
322
|
+
"""Schema definition for OTIDV / Other Transaction IDV (not used for instances)"""
|
|
323
|
+
|
|
324
|
+
key: str
|
|
325
|
+
piid: str | None = None
|
|
326
|
+
award_date: date | None = None
|
|
327
|
+
description: str | None = None
|
|
328
|
+
recipient: RecipientProfile | None = None
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@dataclass
|
|
332
|
+
class Subaward:
|
|
333
|
+
"""Schema definition for Subaward (not used for instances)"""
|
|
334
|
+
|
|
335
|
+
id: str | None = None
|
|
336
|
+
award_key: str | None = None
|
|
337
|
+
prime_uei: str | None = None
|
|
338
|
+
sub_uei: str | None = None
|
|
339
|
+
amount: Decimal | None = None
|
|
340
|
+
|
|
341
|
+
|
|
298
342
|
@dataclass
|
|
299
343
|
class Vehicle:
|
|
300
344
|
"""Schema definition for Vehicle (not used for instances)"""
|
|
@@ -563,3 +607,20 @@ class ShapeConfig:
|
|
|
563
607
|
|
|
564
608
|
# Default for list_vehicle_awardees()
|
|
565
609
|
VEHICLE_AWARDEES_MINIMAL: Final = "uuid,key,piid,award_date,title,order_count,idv_obligations,idv_contracts_value,recipient(display_name,uei)"
|
|
610
|
+
|
|
611
|
+
# Default for list_organizations()
|
|
612
|
+
ORGANIZATIONS_MINIMAL: Final = "key,fh_key,name,level,type,short_name"
|
|
613
|
+
|
|
614
|
+
# Default for list_otas()
|
|
615
|
+
OTAS_MINIMAL: Final = (
|
|
616
|
+
"key,piid,award_date,recipient(display_name,uei),description,total_contract_value,obligated"
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
# Default for list_otidvs()
|
|
620
|
+
OTIDVS_MINIMAL: Final = "key,piid,award_date,recipient(display_name,uei),description,total_contract_value,obligated,idv_type"
|
|
621
|
+
|
|
622
|
+
# Default for list_subawards()
|
|
623
|
+
# Note: API does not accept "id" or "amount" in shape (unknown_field). Use only accepted fields.
|
|
624
|
+
SUBAWARDS_MINIMAL: Final = (
|
|
625
|
+
"award_key,prime_recipient(uei,display_name),subaward_recipient(uei,display_name)"
|
|
626
|
+
)
|
tango/shapes/explicit_schemas.py
CHANGED
|
@@ -975,6 +975,76 @@ VEHICLE_SCHEMA: dict[str, FieldSchema] = {
|
|
|
975
975
|
}
|
|
976
976
|
|
|
977
977
|
|
|
978
|
+
# Organization (agencies hierarchy)
|
|
979
|
+
ORGANIZATION_SCHEMA: dict[str, FieldSchema] = {
|
|
980
|
+
"key": FieldSchema(name="key", type=str, is_optional=True, is_list=False),
|
|
981
|
+
"fh_key": FieldSchema(name="fh_key", type=str, is_optional=False, is_list=False),
|
|
982
|
+
"name": FieldSchema(name="name", type=str, is_optional=True, is_list=False),
|
|
983
|
+
"short_name": FieldSchema(name="short_name", type=str, is_optional=True, is_list=False),
|
|
984
|
+
"level": FieldSchema(name="level", type=int, is_optional=True, is_list=False),
|
|
985
|
+
"type": FieldSchema(name="type", type=str, is_optional=True, is_list=False),
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
# OTA (Other Transaction Agreement) - IDV-like
|
|
989
|
+
OTA_SCHEMA: dict[str, FieldSchema] = {
|
|
990
|
+
"key": FieldSchema(name="key", type=str, is_optional=False, is_list=False),
|
|
991
|
+
"piid": FieldSchema(name="piid", type=str, is_optional=True, is_list=False),
|
|
992
|
+
"award_date": FieldSchema(name="award_date", type=date, is_optional=True, is_list=False),
|
|
993
|
+
"description": FieldSchema(name="description", type=str, is_optional=True, is_list=False),
|
|
994
|
+
"total_contract_value": FieldSchema(
|
|
995
|
+
name="total_contract_value", type=Decimal, is_optional=True, is_list=False
|
|
996
|
+
),
|
|
997
|
+
"obligated": FieldSchema(name="obligated", type=Decimal, is_optional=True, is_list=False),
|
|
998
|
+
"recipient": FieldSchema(
|
|
999
|
+
name="recipient",
|
|
1000
|
+
type=dict,
|
|
1001
|
+
is_optional=True,
|
|
1002
|
+
is_list=False,
|
|
1003
|
+
nested_model="RecipientProfile",
|
|
1004
|
+
),
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
# OTIDV (Other Transaction IDV) - IDV-like
|
|
1008
|
+
OTIDV_SCHEMA: dict[str, FieldSchema] = {
|
|
1009
|
+
"key": FieldSchema(name="key", type=str, is_optional=False, is_list=False),
|
|
1010
|
+
"piid": FieldSchema(name="piid", type=str, is_optional=True, is_list=False),
|
|
1011
|
+
"award_date": FieldSchema(name="award_date", type=date, is_optional=True, is_list=False),
|
|
1012
|
+
"description": FieldSchema(name="description", type=str, is_optional=True, is_list=False),
|
|
1013
|
+
"total_contract_value": FieldSchema(
|
|
1014
|
+
name="total_contract_value", type=Decimal, is_optional=True, is_list=False
|
|
1015
|
+
),
|
|
1016
|
+
"obligated": FieldSchema(name="obligated", type=Decimal, is_optional=True, is_list=False),
|
|
1017
|
+
"idv_type": FieldSchema(name="idv_type", type=dict, is_optional=True, is_list=False),
|
|
1018
|
+
"recipient": FieldSchema(
|
|
1019
|
+
name="recipient",
|
|
1020
|
+
type=dict,
|
|
1021
|
+
is_optional=True,
|
|
1022
|
+
is_list=False,
|
|
1023
|
+
nested_model="RecipientProfile",
|
|
1024
|
+
),
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
# Subaward (prime/sub awards)
|
|
1028
|
+
SUBAWARD_SCHEMA: dict[str, FieldSchema] = {
|
|
1029
|
+
"id": FieldSchema(name="id", type=str, is_optional=True, is_list=False),
|
|
1030
|
+
"award_key": FieldSchema(name="award_key", type=str, is_optional=True, is_list=False),
|
|
1031
|
+
"amount": FieldSchema(name="amount", type=Decimal, is_optional=True, is_list=False),
|
|
1032
|
+
"prime_recipient": FieldSchema(
|
|
1033
|
+
name="prime_recipient",
|
|
1034
|
+
type=dict,
|
|
1035
|
+
is_optional=True,
|
|
1036
|
+
is_list=False,
|
|
1037
|
+
nested_model="RecipientProfile",
|
|
1038
|
+
),
|
|
1039
|
+
"subaward_recipient": FieldSchema(
|
|
1040
|
+
name="subaward_recipient",
|
|
1041
|
+
type=dict,
|
|
1042
|
+
is_optional=True,
|
|
1043
|
+
is_list=False,
|
|
1044
|
+
nested_model="RecipientProfile",
|
|
1045
|
+
),
|
|
1046
|
+
}
|
|
1047
|
+
|
|
978
1048
|
# ============================================================================
|
|
979
1049
|
# SCHEMA REGISTRY MAPPING
|
|
980
1050
|
# ============================================================================
|
|
@@ -1009,6 +1079,11 @@ EXPLICIT_SCHEMAS: dict[str, dict[str, FieldSchema]] = {
|
|
|
1009
1079
|
"CFDANumber": CFDA_NUMBER_SCHEMA,
|
|
1010
1080
|
"CodeDescription": CODE_DESCRIPTION_SCHEMA,
|
|
1011
1081
|
"GrantAttachment": GRANT_ATTACHMENT_SCHEMA,
|
|
1082
|
+
# Additional list endpoints
|
|
1083
|
+
"Organization": ORGANIZATION_SCHEMA,
|
|
1084
|
+
"OTA": OTA_SCHEMA,
|
|
1085
|
+
"OTIDV": OTIDV_SCHEMA,
|
|
1086
|
+
"Subaward": SUBAWARD_SCHEMA,
|
|
1012
1087
|
}
|
|
1013
1088
|
|
|
1014
1089
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tango-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.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
|
|
@@ -487,8 +487,10 @@ tango-python/
|
|
|
487
487
|
│ └── quick_start.ipynb # Interactive quick start
|
|
488
488
|
├── scripts/ # Utility scripts
|
|
489
489
|
│ ├── README.md
|
|
490
|
+
│ ├── check_filter_shape_conformance.py # Filter + shape conformance (CI)
|
|
490
491
|
│ ├── fetch_api_schema.py
|
|
491
|
-
│
|
|
492
|
+
│ ├── generate_schemas_from_api.py
|
|
493
|
+
│ └── pr_review.py # PR validation (lint, types, tests, conformance)
|
|
492
494
|
├── pyproject.toml # Project configuration
|
|
493
495
|
├── uv.lock # Dependency lock file
|
|
494
496
|
├── LICENSE # MIT License
|
|
@@ -526,7 +528,12 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
526
528
|
|
|
527
529
|
1. Fork the repository
|
|
528
530
|
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
529
|
-
3. Run
|
|
530
|
-
4.
|
|
531
|
-
5.
|
|
532
|
-
6.
|
|
531
|
+
3. Run lint and format: `uv run ruff format tango/ && uv run ruff check tango/`
|
|
532
|
+
4. Run type checking: `uv run mypy tango/`
|
|
533
|
+
5. Run tests: `uv run pytest`
|
|
534
|
+
6. (Optional) Run [filter and shape conformance](scripts/README.md#filter-and-shape-conformance) if you have the tango API manifest; CI will run it on push/PR
|
|
535
|
+
7. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
536
|
+
8. Push to the branch (`git push origin feature/amazing-feature`)
|
|
537
|
+
9. Open a Pull Request
|
|
538
|
+
|
|
539
|
+
For a single command that runs formatting, linting, type checking, and tests (and conformance when the manifest is present), use: `uv run python scripts/pr_review.py --mode full`
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
tango/__init__.py,sha256=
|
|
2
|
-
tango/client.py,sha256=
|
|
1
|
+
tango/__init__.py,sha256=PahQcpEfXRycCJhXaWm2-9AbfcdUKB8CC6EcN3AsBqc,1050
|
|
2
|
+
tango/client.py,sha256=Y3ZsOCHX3zhrx7qjHWnQNIfLXidffxty1pBvY0yVrH0,71719
|
|
3
3
|
tango/exceptions.py,sha256=JmtbOY0ofBnX24pUErh2XFlTj9dim2ngyboserEGRFw,2226
|
|
4
|
-
tango/models.py,sha256=
|
|
4
|
+
tango/models.py,sha256=uEdNdEN40BAJ9OSZyoRE7L0UemSPzrQto80OZYeCVYE,19008
|
|
5
5
|
tango/shapes/__init__.py,sha256=7ea1WU74jp4znhNw-gXruag6m6eyPZtbVgbDFmFUWro,1072
|
|
6
|
-
tango/shapes/explicit_schemas.py,sha256=
|
|
6
|
+
tango/shapes/explicit_schemas.py,sha256=32g47TIdrJpNzlJfxg_OKu5H2o1l_3Z7JTDVf2bA4N8,50219
|
|
7
7
|
tango/shapes/factory.py,sha256=ytpMi5Uw72XZ8MimhuSsLDVXF3zO_Zt3_tAL6NF7LnU,34318
|
|
8
8
|
tango/shapes/generator.py,sha256=61V1T3lm8Ps_KSMJAezQJLQVFbNKt1jtoLyhiqNtFTs,23380
|
|
9
9
|
tango/shapes/models.py,sha256=h3pIhOqrrdlN953Y6r0oney5HFbKPOD-frRndRWimJ0,3018
|
|
10
10
|
tango/shapes/parser.py,sha256=k6OsI2w3GH6-IBbc-XTLgL1mWH7bMf7A_dA6pr1xKfw,24619
|
|
11
11
|
tango/shapes/schema.py,sha256=VRPOB1sBdjFyimNchrZKIpTHn83CyX4RfU9077aQtIU,14136
|
|
12
12
|
tango/shapes/types.py,sha256=27jrAE0VIdrKaLjR_FK71hfIIGX2Tg3ex7REEBV1TFE,1301
|
|
13
|
-
tango_python-0.
|
|
14
|
-
tango_python-0.
|
|
15
|
-
tango_python-0.
|
|
16
|
-
tango_python-0.
|
|
13
|
+
tango_python-0.4.0.dist-info/METADATA,sha256=WKMs-pFRxK6ENOUlI8mb6rUbNF95SuZtcVF5io9wSOQ,16210
|
|
14
|
+
tango_python-0.4.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
15
|
+
tango_python-0.4.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
|
|
16
|
+
tango_python-0.4.0.dist-info/RECORD,,
|
|
File without changes
|