Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

## Next Release

- Adds `retrieve_stateless_rates` function to pull stateless rates when shipment data is provided
- Adds `get_lowest_stateless_rate` function to filter the lowest stateless rate

## v7.9.0 (2023-01-18)

- Adds `all` function to `Pickup` to retrieve all pickups
Expand Down
1 change: 1 addition & 0 deletions easypost/beta/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# flake8: noqa
from easypost.beta.rate import Rate
from easypost.beta.referral import Referral
26 changes: 26 additions & 0 deletions easypost/beta/rate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import (
Any,
Dict,
Optional,
)

from easypost.easypost_object import convert_to_easypost_object
from easypost.requestor import (
RequestMethod,
Requestor,
)
from easypost.resource import Resource


class Rate(Resource):
@classmethod
def retrieve_stateless_rates(cls, api_key: Optional[str] = None, **params) -> Dict[str, Any]:
"""Retrieves stateless rates by passing shipment data."""
requestor = Requestor(local_api_key=api_key)
url = cls.class_url()
wrapped_params = {
"shipment": params,
}
response, api_key = requestor.request(method=RequestMethod.POST, url=url, params=wrapped_params, beta=True)

return convert_to_easypost_object(response=response.get("rates", None), api_key=api_key)
32 changes: 31 additions & 1 deletion easypost/util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from typing import List
from typing import (
Any,
Dict,
List,
)

from easypost.easypost_object import EasyPostObject
from easypost.error import Error
Expand Down Expand Up @@ -29,3 +33,29 @@ def get_lowest_object_rate(
raise Error(message="No rates found.")

return lowest_rate


def get_lowest_stateless_rate(
stateless_rates: List[Dict[str, Any]], carriers: List[str] = None, services: List[str] = None
) -> Dict[str, Any]:
"""Get the lowest stateless rate."""
carriers = carriers or []
services = services or []
lowest_rate = None

carriers = [carrier.lower() for carrier in carriers]
services = [service.lower() for service in services]

for rate in stateless_rates:
if (carriers and rate["carrier"].lower() not in carriers) or (
services and rate["service"].lower() not in services
):
continue

if lowest_rate is None or float(rate.rate) < float(lowest_rate.rate):
lowest_rate = rate

if lowest_rate is None:
raise Error(message="No rates found.")

return lowest_rate
105 changes: 105 additions & 0 deletions tests/cassettes/test_beta_get_lowest_stateless_rate.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

107 changes: 107 additions & 0 deletions tests/cassettes/test_beta_retrieve_stateless_rates.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions tests/test_beta_rate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest

import easypost
from easypost.util import get_lowest_stateless_rate


@pytest.mark.vcr()
def test_beta_retrieve_stateless_rates(basic_shipment):
"""Tests that we can retrieve stateless rates when basic shipment data."""
stateless_rates = easypost.beta.Rate.retrieve_stateless_rates(**basic_shipment)

assert all(rate["object"] == "Rate" for rate in stateless_rates)


@pytest.mark.vcr()
def test_beta_get_lowest_stateless_rate(basic_shipment):
"""Tests that we can return the lowest stateless rate from a list of stateless rates."""
stateless_rates = easypost.beta.Rate.retrieve_stateless_rates(**basic_shipment)

lowest_stateless_rate = get_lowest_stateless_rate(stateless_rates)

assert lowest_stateless_rate["service"] == "First"