tango-python 1.1.2__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tango/__init__.py CHANGED
@@ -44,7 +44,7 @@ from .webhooks import (
44
44
  )
45
45
  from .webhooks.receiver import Delivery, WebhookReceiver
46
46
 
47
- __version__ = "1.1.2"
47
+ __version__ = "1.2.0"
48
48
  __all__ = [
49
49
  "TangoClient",
50
50
  "TangoAPIError",
tango/client.py CHANGED
@@ -1923,15 +1923,37 @@ class TangoClient:
1923
1923
  )
1924
1924
 
1925
1925
  # Business Types endpoints
1926
- def list_business_types(self, page: int = 1, limit: int = 25) -> PaginatedResponse:
1927
- """List business types"""
1928
- params = {"page": page, "limit": min(limit, 100)}
1926
+ def list_business_types(
1927
+ self,
1928
+ page: int = 1,
1929
+ limit: int = 25,
1930
+ shape: str | None = None,
1931
+ flat: bool = False,
1932
+ flat_lists: bool = False,
1933
+ ) -> PaginatedResponse:
1934
+ """List business types.
1935
+
1936
+ When ``shape`` is omitted the API applies its own default
1937
+ (``name,code``) and results are returned as :class:`BusinessType`
1938
+ instances. When ``shape`` is provided, raw dicts are returned so the
1939
+ caller can rely on the exact shape requested.
1940
+ """
1941
+ params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
1942
+ if shape:
1943
+ params["shape"] = shape
1944
+ if flat:
1945
+ params["flat"] = "true"
1946
+ if flat_lists:
1947
+ params["flat_lists"] = "true"
1929
1948
  data = self._get("/api/business_types/", params)
1949
+ results: list[Any] = (
1950
+ list(data["results"]) if shape else [BusinessType(**btype) for btype in data["results"]]
1951
+ )
1930
1952
  return PaginatedResponse(
1931
1953
  count=data["count"],
1932
1954
  next=data.get("next"),
1933
1955
  previous=data.get("previous"),
1934
- results=[BusinessType(**btype) for btype in data["results"]],
1956
+ results=results,
1935
1957
  )
1936
1958
 
1937
1959
  def list_naics(
@@ -1945,8 +1967,17 @@ class TangoClient:
1945
1967
  revenue_limit_gte: int | None = None,
1946
1968
  revenue_limit_lte: int | None = None,
1947
1969
  search: str | None = None,
1970
+ shape: str | None = None,
1971
+ flat: bool = False,
1972
+ flat_lists: bool = False,
1948
1973
  ) -> PaginatedResponse:
1949
- """List NAICS codes (`/api/naics/`)."""
1974
+ """List NAICS codes (`/api/naics/`).
1975
+
1976
+ When ``shape`` is omitted the API applies its own default. Passing any
1977
+ of the ``revenue_limit*`` / ``employee_limit*`` filters causes the API
1978
+ to widen the default shape to include ``size_standards`` and
1979
+ ``federal_obligations``.
1980
+ """
1950
1981
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
1951
1982
  if employee_limit is not None:
1952
1983
  params["employee_limit"] = employee_limit
@@ -1962,6 +1993,12 @@ class TangoClient:
1962
1993
  params["revenue_limit_lte"] = revenue_limit_lte
1963
1994
  if search is not None:
1964
1995
  params["search"] = search
1996
+ if shape:
1997
+ params["shape"] = shape
1998
+ if flat:
1999
+ params["flat"] = "true"
2000
+ if flat_lists:
2001
+ params["flat_lists"] = "true"
1965
2002
  data = self._get("/api/naics/", params)
1966
2003
  return PaginatedResponse(
1967
2004
  count=data.get("count", 0),
@@ -2612,6 +2649,85 @@ class TangoClient:
2612
2649
  bea_category: str | None = None,
2613
2650
  on_off_budget: str | None = None,
2614
2651
  subfunction_code: str | None = None,
2652
+ # Range-numeric filters: each field also accepts ``__gte`` / ``__lte``.
2653
+ requested_ba: float | None = None,
2654
+ requested_ba_gte: float | None = None,
2655
+ requested_ba_lte: float | None = None,
2656
+ enacted_ba: float | None = None,
2657
+ enacted_ba_gte: float | None = None,
2658
+ enacted_ba_lte: float | None = None,
2659
+ apportioned: float | None = None,
2660
+ apportioned_gte: float | None = None,
2661
+ apportioned_lte: float | None = None,
2662
+ obligated_total: float | None = None,
2663
+ obligated_total_gte: float | None = None,
2664
+ obligated_total_lte: float | None = None,
2665
+ outlayed_total: float | None = None,
2666
+ outlayed_total_gte: float | None = None,
2667
+ outlayed_total_lte: float | None = None,
2668
+ unobligated_balance: float | None = None,
2669
+ unobligated_balance_gte: float | None = None,
2670
+ unobligated_balance_lte: float | None = None,
2671
+ contract_obligated: float | None = None,
2672
+ contract_obligated_gte: float | None = None,
2673
+ contract_obligated_lte: float | None = None,
2674
+ contract_outlayed: float | None = None,
2675
+ contract_outlayed_gte: float | None = None,
2676
+ contract_outlayed_lte: float | None = None,
2677
+ assistance_obligated: float | None = None,
2678
+ assistance_obligated_gte: float | None = None,
2679
+ assistance_obligated_lte: float | None = None,
2680
+ assistance_outlayed: float | None = None,
2681
+ assistance_outlayed_gte: float | None = None,
2682
+ assistance_outlayed_lte: float | None = None,
2683
+ contract_share_of_obligated_capped: float | None = None,
2684
+ contract_share_of_obligated_capped_gte: float | None = None,
2685
+ contract_share_of_obligated_capped_lte: float | None = None,
2686
+ obligated_to_apportioned_pct: float | None = None,
2687
+ obligated_to_apportioned_pct_gte: float | None = None,
2688
+ obligated_to_apportioned_pct_lte: float | None = None,
2689
+ obligated_to_apportioned_pct_capped: float | None = None,
2690
+ obligated_to_apportioned_pct_capped_gte: float | None = None,
2691
+ obligated_to_apportioned_pct_capped_lte: float | None = None,
2692
+ apportioned_to_enacted_pct: float | None = None,
2693
+ apportioned_to_enacted_pct_gte: float | None = None,
2694
+ apportioned_to_enacted_pct_lte: float | None = None,
2695
+ apportioned_to_enacted_pct_capped: float | None = None,
2696
+ apportioned_to_enacted_pct_capped_gte: float | None = None,
2697
+ apportioned_to_enacted_pct_capped_lte: float | None = None,
2698
+ obligated_to_enacted_pct: float | None = None,
2699
+ obligated_to_enacted_pct_gte: float | None = None,
2700
+ obligated_to_enacted_pct_lte: float | None = None,
2701
+ obligated_to_enacted_pct_capped: float | None = None,
2702
+ obligated_to_enacted_pct_capped_gte: float | None = None,
2703
+ obligated_to_enacted_pct_capped_lte: float | None = None,
2704
+ outlayed_to_obligated_pct: float | None = None,
2705
+ outlayed_to_obligated_pct_gte: float | None = None,
2706
+ outlayed_to_obligated_pct_lte: float | None = None,
2707
+ outlayed_to_obligated_pct_capped: float | None = None,
2708
+ outlayed_to_obligated_pct_capped_gte: float | None = None,
2709
+ outlayed_to_obligated_pct_capped_lte: float | None = None,
2710
+ unobligated_pct: float | None = None,
2711
+ unobligated_pct_gte: float | None = None,
2712
+ unobligated_pct_lte: float | None = None,
2713
+ enacted_ba_yoy_pct: float | None = None,
2714
+ enacted_ba_yoy_pct_gte: float | None = None,
2715
+ enacted_ba_yoy_pct_lte: float | None = None,
2716
+ obligated_yoy_pct: float | None = None,
2717
+ obligated_yoy_pct_gte: float | None = None,
2718
+ obligated_yoy_pct_lte: float | None = None,
2719
+ enacted_ba_5yr_cagr: float | None = None,
2720
+ enacted_ba_5yr_cagr_gte: float | None = None,
2721
+ enacted_ba_5yr_cagr_lte: float | None = None,
2722
+ ba_growth_next_year_pct: float | None = None,
2723
+ ba_growth_next_year_pct_gte: float | None = None,
2724
+ ba_growth_next_year_pct_lte: float | None = None,
2725
+ actual_vs_requested_contract: float | None = None,
2726
+ actual_vs_requested_contract_gte: float | None = None,
2727
+ actual_vs_requested_contract_lte: float | None = None,
2728
+ actual_vs_requested_contract_capped: float | None = None,
2729
+ actual_vs_requested_contract_capped_gte: float | None = None,
2730
+ actual_vs_requested_contract_capped_lte: float | None = None,
2615
2731
  search: str | None = None,
2616
2732
  ordering: str | None = None,
2617
2733
  ) -> PaginatedResponse:
@@ -2637,8 +2753,49 @@ class TangoClient:
2637
2753
  bea_category: BEA category (exact).
2638
2754
  on_off_budget: On/off budget flag (exact).
2639
2755
  subfunction_code: Subfunction code (exact).
2756
+ requested_ba: President's-budget requested BA (exact). Also
2757
+ ``requested_ba_gte`` / ``requested_ba_lte`` for range queries.
2758
+ enacted_ba: Enacted budget authority (exact / gte / lte).
2759
+ apportioned: Apportioned amount (exact / gte / lte).
2760
+ obligated_total: Total obligated (exact / gte / lte).
2761
+ outlayed_total: Total outlayed (exact / gte / lte).
2762
+ unobligated_balance: Apportioned minus obligated, in dollars
2763
+ (exact / gte / lte). Use ``__gte`` to surface accounts with
2764
+ appropriated headroom that hasn't yet hit contract.
2765
+ contract_obligated: Contract-only obligated (exact / gte / lte).
2766
+ contract_outlayed: Contract-only outlayed (exact / gte / lte).
2767
+ assistance_obligated: Assistance-only obligated (exact / gte / lte).
2768
+ assistance_outlayed: Assistance-only outlayed (exact / gte / lte).
2769
+ contract_share_of_obligated_capped: Contracts as share of
2770
+ obligated, capped at 1.0 (exact / gte / lte). Use ``__gte`` to
2771
+ filter to contract-heavy accounts.
2772
+ obligated_to_apportioned_pct: Burn ratio
2773
+ (obligated / apportioned). Also ``_capped`` variant capped at
2774
+ 1.0. Both expose exact / gte / lte.
2775
+ apportioned_to_enacted_pct: Apportionment ratio
2776
+ (apportioned / enacted). Also ``_capped`` variant. Exact / gte / lte.
2777
+ obligated_to_enacted_pct: Obligated-to-enacted ratio. Also
2778
+ ``_capped`` variant. Exact / gte / lte.
2779
+ outlayed_to_obligated_pct: Spendout ratio (outlayed / obligated).
2780
+ Also ``_capped`` variant. Exact / gte / lte.
2781
+ unobligated_pct: Unobligated share of apportioned (exact / gte / lte).
2782
+ enacted_ba_yoy_pct: Year-over-year enacted BA growth
2783
+ (exact / gte / lte).
2784
+ obligated_yoy_pct: Year-over-year obligated growth
2785
+ (exact / gte / lte).
2786
+ enacted_ba_5yr_cagr: 5-year compound annual growth of enacted BA
2787
+ (exact / gte / lte).
2788
+ ba_growth_next_year_pct: Next-year requested BA growth
2789
+ (exact / gte / lte). Use ``__gte`` for forward-looking
2790
+ pipeline discovery.
2791
+ actual_vs_requested_contract: Realization ratio of contract
2792
+ obligated against the prior-year request (exact / gte / lte).
2793
+ Also ``_capped`` variant.
2640
2794
  search: Full-text search over account_title/agency_name/bureau_name.
2641
- ordering: Sort field (prefix with '-' for descending).
2795
+ ordering: Sort field (prefix with '-' for descending). Any of the
2796
+ numeric fields above is a valid ordering target — e.g.
2797
+ ``ordering="-unobligated_balance"`` to rank by largest
2798
+ headroom first.
2642
2799
  """
2643
2800
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
2644
2801
  if shape is None:
@@ -2649,7 +2806,7 @@ class TangoClient:
2649
2806
  params["flat"] = "true"
2650
2807
  if flat_lists:
2651
2808
  params["flat_lists"] = "true"
2652
- for key, val in (
2809
+ scalar_filters: tuple[tuple[str, Any], ...] = (
2653
2810
  ("federal_account_symbol", federal_account_symbol),
2654
2811
  ("fiscal_year", fiscal_year),
2655
2812
  ("fiscal_year__gte", fiscal_year_gte),
@@ -2662,9 +2819,146 @@ class TangoClient:
2662
2819
  ("subfunction_code", subfunction_code),
2663
2820
  ("search", search),
2664
2821
  ("ordering", ordering),
2665
- ):
2822
+ )
2823
+ for key, val in scalar_filters:
2666
2824
  if val is not None:
2667
2825
  params[key] = val
2826
+ # Range-numeric filters: each field has exact / __gte / __lte forms.
2827
+ range_filters: tuple[tuple[str, float | None, float | None, float | None], ...] = (
2828
+ ("requested_ba", requested_ba, requested_ba_gte, requested_ba_lte),
2829
+ ("enacted_ba", enacted_ba, enacted_ba_gte, enacted_ba_lte),
2830
+ ("apportioned", apportioned, apportioned_gte, apportioned_lte),
2831
+ ("obligated_total", obligated_total, obligated_total_gte, obligated_total_lte),
2832
+ ("outlayed_total", outlayed_total, outlayed_total_gte, outlayed_total_lte),
2833
+ (
2834
+ "unobligated_balance",
2835
+ unobligated_balance,
2836
+ unobligated_balance_gte,
2837
+ unobligated_balance_lte,
2838
+ ),
2839
+ (
2840
+ "contract_obligated",
2841
+ contract_obligated,
2842
+ contract_obligated_gte,
2843
+ contract_obligated_lte,
2844
+ ),
2845
+ (
2846
+ "contract_outlayed",
2847
+ contract_outlayed,
2848
+ contract_outlayed_gte,
2849
+ contract_outlayed_lte,
2850
+ ),
2851
+ (
2852
+ "assistance_obligated",
2853
+ assistance_obligated,
2854
+ assistance_obligated_gte,
2855
+ assistance_obligated_lte,
2856
+ ),
2857
+ (
2858
+ "assistance_outlayed",
2859
+ assistance_outlayed,
2860
+ assistance_outlayed_gte,
2861
+ assistance_outlayed_lte,
2862
+ ),
2863
+ (
2864
+ "contract_share_of_obligated_capped",
2865
+ contract_share_of_obligated_capped,
2866
+ contract_share_of_obligated_capped_gte,
2867
+ contract_share_of_obligated_capped_lte,
2868
+ ),
2869
+ (
2870
+ "obligated_to_apportioned_pct",
2871
+ obligated_to_apportioned_pct,
2872
+ obligated_to_apportioned_pct_gte,
2873
+ obligated_to_apportioned_pct_lte,
2874
+ ),
2875
+ (
2876
+ "obligated_to_apportioned_pct_capped",
2877
+ obligated_to_apportioned_pct_capped,
2878
+ obligated_to_apportioned_pct_capped_gte,
2879
+ obligated_to_apportioned_pct_capped_lte,
2880
+ ),
2881
+ (
2882
+ "apportioned_to_enacted_pct",
2883
+ apportioned_to_enacted_pct,
2884
+ apportioned_to_enacted_pct_gte,
2885
+ apportioned_to_enacted_pct_lte,
2886
+ ),
2887
+ (
2888
+ "apportioned_to_enacted_pct_capped",
2889
+ apportioned_to_enacted_pct_capped,
2890
+ apportioned_to_enacted_pct_capped_gte,
2891
+ apportioned_to_enacted_pct_capped_lte,
2892
+ ),
2893
+ (
2894
+ "obligated_to_enacted_pct",
2895
+ obligated_to_enacted_pct,
2896
+ obligated_to_enacted_pct_gte,
2897
+ obligated_to_enacted_pct_lte,
2898
+ ),
2899
+ (
2900
+ "obligated_to_enacted_pct_capped",
2901
+ obligated_to_enacted_pct_capped,
2902
+ obligated_to_enacted_pct_capped_gte,
2903
+ obligated_to_enacted_pct_capped_lte,
2904
+ ),
2905
+ (
2906
+ "outlayed_to_obligated_pct",
2907
+ outlayed_to_obligated_pct,
2908
+ outlayed_to_obligated_pct_gte,
2909
+ outlayed_to_obligated_pct_lte,
2910
+ ),
2911
+ (
2912
+ "outlayed_to_obligated_pct_capped",
2913
+ outlayed_to_obligated_pct_capped,
2914
+ outlayed_to_obligated_pct_capped_gte,
2915
+ outlayed_to_obligated_pct_capped_lte,
2916
+ ),
2917
+ ("unobligated_pct", unobligated_pct, unobligated_pct_gte, unobligated_pct_lte),
2918
+ (
2919
+ "enacted_ba_yoy_pct",
2920
+ enacted_ba_yoy_pct,
2921
+ enacted_ba_yoy_pct_gte,
2922
+ enacted_ba_yoy_pct_lte,
2923
+ ),
2924
+ (
2925
+ "obligated_yoy_pct",
2926
+ obligated_yoy_pct,
2927
+ obligated_yoy_pct_gte,
2928
+ obligated_yoy_pct_lte,
2929
+ ),
2930
+ (
2931
+ "enacted_ba_5yr_cagr",
2932
+ enacted_ba_5yr_cagr,
2933
+ enacted_ba_5yr_cagr_gte,
2934
+ enacted_ba_5yr_cagr_lte,
2935
+ ),
2936
+ (
2937
+ "ba_growth_next_year_pct",
2938
+ ba_growth_next_year_pct,
2939
+ ba_growth_next_year_pct_gte,
2940
+ ba_growth_next_year_pct_lte,
2941
+ ),
2942
+ (
2943
+ "actual_vs_requested_contract",
2944
+ actual_vs_requested_contract,
2945
+ actual_vs_requested_contract_gte,
2946
+ actual_vs_requested_contract_lte,
2947
+ ),
2948
+ (
2949
+ "actual_vs_requested_contract_capped",
2950
+ actual_vs_requested_contract_capped,
2951
+ actual_vs_requested_contract_capped_gte,
2952
+ actual_vs_requested_contract_capped_lte,
2953
+ ),
2954
+ )
2955
+ for field, exact, gte, lte in range_filters:
2956
+ if exact is not None:
2957
+ params[field] = exact
2958
+ if gte is not None:
2959
+ params[f"{field}__gte"] = gte
2960
+ if lte is not None:
2961
+ params[f"{field}__lte"] = lte
2668
2962
  data = self._get("/api/budget/accounts/", params)
2669
2963
  results = [
2670
2964
  self._parse_response_with_shape(obj, shape, BudgetAccount, flat, flat_lists)
@@ -3344,9 +3638,22 @@ class TangoClient:
3344
3638
  raise TangoValidationError("Department code is required")
3345
3639
  return self._get(f"/api/departments/{code}/")
3346
3640
 
3347
- def list_psc(self, page: int = 1, limit: int = 25) -> PaginatedResponse[dict[str, Any]]:
3641
+ def list_psc(
3642
+ self,
3643
+ page: int = 1,
3644
+ limit: int = 25,
3645
+ shape: str | None = None,
3646
+ flat: bool = False,
3647
+ flat_lists: bool = False,
3648
+ ) -> PaginatedResponse[dict[str, Any]]:
3348
3649
  """List Product Service Codes (`/api/psc/`)."""
3349
3650
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
3651
+ if shape:
3652
+ params["shape"] = shape
3653
+ if flat:
3654
+ params["flat"] = "true"
3655
+ if flat_lists:
3656
+ params["flat_lists"] = "true"
3350
3657
  data = self._get("/api/psc/", params)
3351
3658
  return PaginatedResponse(
3352
3659
  count=int(data.get("count", 0)),
@@ -3355,11 +3662,24 @@ class TangoClient:
3355
3662
  results=list(data.get("results") or []),
3356
3663
  )
3357
3664
 
3358
- def get_psc(self, code: str) -> dict[str, Any]:
3665
+ def get_psc(
3666
+ self,
3667
+ code: str,
3668
+ shape: str | None = None,
3669
+ flat: bool = False,
3670
+ flat_lists: bool = False,
3671
+ ) -> dict[str, Any]:
3359
3672
  """Get a Product Service Code by code (`/api/psc/{code}/`)."""
3360
3673
  if not code:
3361
3674
  raise TangoValidationError("PSC code is required")
3362
- return self._get(f"/api/psc/{code}/")
3675
+ params: dict[str, Any] = {}
3676
+ if shape:
3677
+ params["shape"] = shape
3678
+ if flat:
3679
+ params["flat"] = "true"
3680
+ if flat_lists:
3681
+ params["flat_lists"] = "true"
3682
+ return self._get(f"/api/psc/{code}/", params)
3363
3683
 
3364
3684
  def get_psc_metrics(self, code: str, months: int, period_grouping: str) -> dict[str, Any]:
3365
3685
  """Get rolling PSC metrics (`/api/psc/{code}/metrics/{months}/{period_grouping}/`).
@@ -3374,11 +3694,24 @@ class TangoClient:
3374
3694
  raise TangoValidationError("PSC code is required")
3375
3695
  return self._get(f"/api/psc/{code}/metrics/{months}/{period_grouping}/")
3376
3696
 
3377
- def get_naics(self, code: str) -> dict[str, Any]:
3697
+ def get_naics(
3698
+ self,
3699
+ code: str,
3700
+ shape: str | None = None,
3701
+ flat: bool = False,
3702
+ flat_lists: bool = False,
3703
+ ) -> dict[str, Any]:
3378
3704
  """Get a NAICS code by code (`/api/naics/{code}/`)."""
3379
3705
  if not code:
3380
3706
  raise TangoValidationError("NAICS code is required")
3381
- return self._get(f"/api/naics/{code}/")
3707
+ params: dict[str, Any] = {}
3708
+ if shape:
3709
+ params["shape"] = shape
3710
+ if flat:
3711
+ params["flat"] = "true"
3712
+ if flat_lists:
3713
+ params["flat_lists"] = "true"
3714
+ return self._get(f"/api/naics/{code}/", params)
3382
3715
 
3383
3716
  def get_naics_metrics(self, code: str, months: int, period_grouping: str) -> dict[str, Any]:
3384
3717
  """Get rolling NAICS metrics (`/api/naics/{code}/metrics/{months}/{period_grouping}/`)."""
@@ -3386,17 +3719,41 @@ class TangoClient:
3386
3719
  raise TangoValidationError("NAICS code is required")
3387
3720
  return self._get(f"/api/naics/{code}/metrics/{months}/{period_grouping}/")
3388
3721
 
3389
- def get_business_type(self, code: str) -> dict[str, Any]:
3722
+ def get_business_type(
3723
+ self,
3724
+ code: str,
3725
+ shape: str | None = None,
3726
+ flat: bool = False,
3727
+ flat_lists: bool = False,
3728
+ ) -> dict[str, Any]:
3390
3729
  """Get a business type by code (`/api/business_types/{code}/`)."""
3391
3730
  if not code:
3392
3731
  raise TangoValidationError("Business type code is required")
3393
- return self._get(f"/api/business_types/{code}/")
3732
+ params: dict[str, Any] = {}
3733
+ if shape:
3734
+ params["shape"] = shape
3735
+ if flat:
3736
+ params["flat"] = "true"
3737
+ if flat_lists:
3738
+ params["flat_lists"] = "true"
3739
+ return self._get(f"/api/business_types/{code}/", params)
3394
3740
 
3395
3741
  def list_assistance_listings(
3396
- self, page: int = 1, limit: int = 25
3742
+ self,
3743
+ page: int = 1,
3744
+ limit: int = 25,
3745
+ shape: str | None = None,
3746
+ flat: bool = False,
3747
+ flat_lists: bool = False,
3397
3748
  ) -> PaginatedResponse[dict[str, Any]]:
3398
3749
  """List Assistance Listings (CFDA programs) (`/api/assistance_listings/`)."""
3399
3750
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
3751
+ if shape:
3752
+ params["shape"] = shape
3753
+ if flat:
3754
+ params["flat"] = "true"
3755
+ if flat_lists:
3756
+ params["flat_lists"] = "true"
3400
3757
  data = self._get("/api/assistance_listings/", params)
3401
3758
  return PaginatedResponse(
3402
3759
  count=int(data.get("count", 0)),
@@ -3405,22 +3762,44 @@ class TangoClient:
3405
3762
  results=list(data.get("results") or []),
3406
3763
  )
3407
3764
 
3408
- def get_assistance_listing(self, number: str) -> dict[str, Any]:
3765
+ def get_assistance_listing(
3766
+ self,
3767
+ number: str,
3768
+ shape: str | None = None,
3769
+ flat: bool = False,
3770
+ flat_lists: bool = False,
3771
+ ) -> dict[str, Any]:
3409
3772
  """Get an Assistance Listing by CFDA number (`/api/assistance_listings/{number}/`)."""
3410
3773
  if not number:
3411
3774
  raise TangoValidationError("Assistance listing number is required")
3412
- return self._get(f"/api/assistance_listings/{number}/")
3775
+ params: dict[str, Any] = {}
3776
+ if shape:
3777
+ params["shape"] = shape
3778
+ if flat:
3779
+ params["flat"] = "true"
3780
+ if flat_lists:
3781
+ params["flat_lists"] = "true"
3782
+ return self._get(f"/api/assistance_listings/{number}/", params)
3413
3783
 
3414
3784
  def list_mas_sins(
3415
3785
  self,
3416
3786
  page: int = 1,
3417
3787
  limit: int = 25,
3418
3788
  search: str | None = None,
3789
+ shape: str | None = None,
3790
+ flat: bool = False,
3791
+ flat_lists: bool = False,
3419
3792
  ) -> PaginatedResponse[dict[str, Any]]:
3420
3793
  """List GSA MAS SINs (`/api/mas_sins/`)."""
3421
3794
  params: dict[str, Any] = {"page": page, "limit": min(limit, 100)}
3422
3795
  if search is not None:
3423
3796
  params["search"] = search
3797
+ if shape:
3798
+ params["shape"] = shape
3799
+ if flat:
3800
+ params["flat"] = "true"
3801
+ if flat_lists:
3802
+ params["flat_lists"] = "true"
3424
3803
  data = self._get("/api/mas_sins/", params)
3425
3804
  return PaginatedResponse(
3426
3805
  count=int(data.get("count", 0)),
@@ -3429,11 +3808,24 @@ class TangoClient:
3429
3808
  results=list(data.get("results") or []),
3430
3809
  )
3431
3810
 
3432
- def get_mas_sin(self, sin: str) -> dict[str, Any]:
3811
+ def get_mas_sin(
3812
+ self,
3813
+ sin: str,
3814
+ shape: str | None = None,
3815
+ flat: bool = False,
3816
+ flat_lists: bool = False,
3817
+ ) -> dict[str, Any]:
3433
3818
  """Get a MAS SIN by code (`/api/mas_sins/{sin}/`)."""
3434
3819
  if not sin:
3435
3820
  raise TangoValidationError("MAS SIN is required")
3436
- return self._get(f"/api/mas_sins/{sin}/")
3821
+ params: dict[str, Any] = {}
3822
+ if shape:
3823
+ params["shape"] = shape
3824
+ if flat:
3825
+ params["flat"] = "true"
3826
+ if flat_lists:
3827
+ params["flat_lists"] = "true"
3828
+ return self._get(f"/api/mas_sins/{sin}/", params)
3437
3829
 
3438
3830
  # ============================================================================
3439
3831
  # Entity sub-resources
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tango-python
3
- Version: 1.1.2
3
+ Version: 1.2.0
4
4
  Summary: Python SDK for the Tango API
5
5
  Project-URL: Homepage, https://github.com/makegov/tango-python
6
6
  Project-URL: Documentation, https://docs.makegov.com/tango-python
@@ -1,5 +1,5 @@
1
- tango/__init__.py,sha256=gg4U46hFhUJwgvbGtOdP3vgooi8US-fLXvpBZ060_H8,1912
2
- tango/client.py,sha256=9-IfMZwerx4uRsPlDI38uQDv77TNl2qShCHVtP4t7EU,144839
1
+ tango/__init__.py,sha256=5IJJ7dqh_g9VN5f59ajqEoWcDoaxuQNcGHezYN5DBr4,1912
2
+ tango/client.py,sha256=sauxEB6UQ7kb7F2BKGB9lBQEXuOjT37lbWXD6OJbiUc,161143
3
3
  tango/exceptions.py,sha256=aRvDm0dUCEtNDfRVYCX7SEDdd1WlIVVY6sN78Tzo-a0,3114
4
4
  tango/models.py,sha256=QqUPtO7HJJDUaJDAMUkzvqR7pj1YIr8BetbS4palxc0,34261
5
5
  tango/shapes/__init__.py,sha256=7ea1WU74jp4znhNw-gXruag6m6eyPZtbVgbDFmFUWro,1072
@@ -15,8 +15,8 @@ tango/webhooks/cli.py,sha256=f_vQbJ2AeSQjKnQo7MxHFyUB2SAKcgHYmGQULS0isqQ,13858
15
15
  tango/webhooks/receiver.py,sha256=5yhsVhlLyoxmOCGvmbynWAIlDB2OaCPVf1H4GA1SxmU,9279
16
16
  tango/webhooks/signing.py,sha256=92Ee-0B6PR7ZkvY3Np3gzl88-mtfKkh-I7lxqCe2lGw,2374
17
17
  tango/webhooks/simulate.py,sha256=g2Osa0FYU5mJuon07T2aUCtmkUoTEzsY261tlp76fF0,3165
18
- tango_python-1.1.2.dist-info/METADATA,sha256=-wGAubnmnT1ysNyUThjVuYWx3p9IjDPf-4FQrxy756U,20599
19
- tango_python-1.1.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
20
- tango_python-1.1.2.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
21
- tango_python-1.1.2.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
22
- tango_python-1.1.2.dist-info/RECORD,,
18
+ tango_python-1.2.0.dist-info/METADATA,sha256=WSJqToZWI1-xEBAXHA89qpr4SUQC4UhmnZqWZhD8pyE,20599
19
+ tango_python-1.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
20
+ tango_python-1.2.0.dist-info/entry_points.txt,sha256=kGLUbglUjuaAqEFvOZ1QuSW0vWb6VeSpCIFKaOFkKoQ,50
21
+ tango_python-1.2.0.dist-info/licenses/LICENSE,sha256=j2kYVHMwTkoGn3ZNScnrdIueG0k2XzB_LCPFoyBc2wk,1064
22
+ tango_python-1.2.0.dist-info/RECORD,,