Skip to content

Commit 4208b7f

Browse files
authored
feat(recommend): add client (#539)
1 parent 880037b commit 4208b7f

File tree

11 files changed

+441
-10
lines changed

11 files changed

+441
-10
lines changed

algoliasearch/helpers_async.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ def closure(*args, **kwargs):
3434

3535
# We make sure we resolve the promise from the raw_responses
3636
if hasattr(result, "raw_responses"):
37-
for (i, raw_response,) in enumerate(result.raw_responses):
37+
for (
38+
i,
39+
raw_response,
40+
) in enumerate(result.raw_responses):
3841
result.raw_responses[i] = yield from raw_response
3942

4043
# We make sure we resolve the promise from the raw_response

algoliasearch/http/requester.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ def send(self, request):
3737

3838
try:
3939
response = self._session.send( # type: ignore
40-
r, timeout=requests_timeout, proxies=request.proxies,
40+
r,
41+
timeout=requests_timeout,
42+
proxies=request.proxies,
4143
)
4244
except Timeout as e:
4345
return Response(error_message=str(e), is_timed_out_error=True)

algoliasearch/recommend_client.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from typing import Optional, Union, List
2+
3+
from algoliasearch.helpers import is_async_available
4+
from algoliasearch.http.request_options import RequestOptions
5+
from algoliasearch.http.verb import Verb
6+
from algoliasearch.configs import SearchConfig
7+
from algoliasearch.http.requester import Requester
8+
from algoliasearch.http.transporter import Transporter
9+
10+
11+
class RecommendClient(object):
12+
@property
13+
def app_id(self):
14+
# type: () -> str
15+
16+
return self._config.app_id
17+
18+
def __init__(self, transporter, search_config):
19+
# type: (Transporter, SearchConfig) -> None
20+
21+
self._transporter = transporter
22+
self._config = search_config
23+
24+
@staticmethod
25+
def create(app_id=None, api_key=None):
26+
# type: (Optional[str], Optional[str]) -> RecommendClient
27+
28+
config = SearchConfig(app_id, api_key)
29+
30+
return RecommendClient.create_with_config(config)
31+
32+
@staticmethod
33+
def create_with_config(config):
34+
# type: (SearchConfig) -> RecommendClient
35+
36+
requester = Requester()
37+
transporter = Transporter(requester, config)
38+
39+
client = RecommendClient(transporter, config)
40+
41+
if is_async_available():
42+
from algoliasearch.recommend_client_async import RecommendClientAsync
43+
from algoliasearch.http.transporter_async import TransporterAsync
44+
from algoliasearch.http.requester_async import RequesterAsync
45+
46+
return RecommendClientAsync(
47+
client, TransporterAsync(RequesterAsync(), config), config
48+
)
49+
50+
return client
51+
52+
def get_recommendations(self, queries, request_options=None):
53+
# type: (List[dict], Optional[Union[dict, RequestOptions]]) -> dict
54+
55+
for q in queries:
56+
if "threshold" not in q or q["threshold"] is None:
57+
q["threshold"] = 0
58+
59+
return self._transporter.read(
60+
Verb.POST,
61+
"1/indexes/*/recommendations",
62+
{"requests": queries},
63+
request_options,
64+
)
65+
66+
def get_related_products(self, queries, request_options=None):
67+
# type: (List[dict], Optional[Union[dict, RequestOptions]]) -> dict
68+
69+
for q in queries:
70+
q["model"] = "related-products"
71+
72+
return self.get_recommendations(queries, request_options)
73+
74+
def get_frequently_bought_together(self, queries, request_options=None):
75+
# type: (List[dict], Optional[Union[dict, RequestOptions]]) -> dict
76+
77+
for q in queries:
78+
q.pop("fallbackParameters", None)
79+
q["model"] = "bought-together"
80+
81+
return self.get_recommendations(queries, request_options)
82+
83+
def close(self):
84+
# type: () -> None
85+
86+
return self._transporter.close() # type: ignore
87+
88+
def _sync(self):
89+
# type: () -> RecommendClient
90+
91+
return self
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from algoliasearch.recommend_client import RecommendClient
2+
from typing import Optional, Type
3+
4+
import types
5+
import asyncio
6+
7+
from algoliasearch.configs import SearchConfig
8+
from algoliasearch.helpers_async import _create_async_methods_in
9+
from algoliasearch.http.transporter_async import TransporterAsync
10+
11+
12+
class RecommendClientAsync(RecommendClient):
13+
def __init__(self, recommend_client, transporter, search_config):
14+
# type: (RecommendClient, TransporterAsync, SearchConfig) -> None
15+
16+
self._recommend_client = recommend_client
17+
self._transporter_async = transporter
18+
19+
super(RecommendClientAsync, self).__init__(
20+
recommend_client._transporter, search_config
21+
)
22+
23+
recommend_client = RecommendClient(transporter, search_config)
24+
recommend_client.__setattr__("_sync", self._sync)
25+
_create_async_methods_in(self, recommend_client)
26+
27+
@asyncio.coroutine
28+
def __aenter__(self):
29+
# type: () -> RecommendClientAsync # type: ignore
30+
31+
return self # type: ignore
32+
33+
@asyncio.coroutine
34+
def __aexit__(self, exc_type, exc, tb): # type: ignore
35+
# type: (Optional[Type[BaseException]], Optional[BaseException],Optional[types.TracebackType]) -> None # noqa: E501
36+
37+
yield from self.close_async() # type: ignore
38+
39+
@asyncio.coroutine
40+
def close_async(self): # type: ignore
41+
# type: () -> None
42+
43+
super().close()
44+
45+
yield from self._transporter_async.close() # type: ignore
46+
47+
def _sync(self):
48+
# type: () -> RecommendClient
49+
50+
return self._recommend_client

algoliasearch/recommendation_client.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ def __init__(self, transporter, config):
1515

1616
warnings.warn(
1717
"`%s.%s` is deprecated, use `%s.%s` instead."
18-
% ("RecommendationClient", "init", "PersonalizationClient", "init",),
18+
% (
19+
"RecommendationClient",
20+
"init",
21+
"PersonalizationClient",
22+
"init",
23+
),
1924
DeprecationWarning,
2025
)
2126

@@ -28,7 +33,12 @@ def create(app_id=None, api_key=None, region=None):
2833

2934
warnings.warn(
3035
"`%s.%s` is deprecated, use `%s.%s` instead."
31-
% ("RecommendationClient", "create", "PersonalizationClient", "create",),
36+
% (
37+
"RecommendationClient",
38+
"create",
39+
"PersonalizationClient",
40+
"create",
41+
),
3242
DeprecationWarning,
3343
)
3444

@@ -115,7 +125,12 @@ def close(self):
115125

116126
warnings.warn(
117127
"`%s.%s` is deprecated, use `%s.%s` instead."
118-
% ("RecommendationClient", "close", "PersonalizationClient", "close",),
128+
% (
129+
"RecommendationClient",
130+
"close",
131+
"PersonalizationClient",
132+
"close",
133+
),
119134
DeprecationWarning,
120135
)
121136

algoliasearch/search_index_async.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ def find_object_async(self, callback, request_options=None): # type: ignore # n
137137
page += 1
138138

139139
def replace_all_objects_async( # type: ignore
140-
self, objects, request_options=None,
140+
self,
141+
objects,
142+
request_options=None,
141143
):
142144
# type: (Union[List[dict], Iterator[dict]], Optional[Union[dict, RequestOptions]]) -> MultipleResponse # noqa: E501
143145

0 commit comments

Comments
 (0)