2018-03-02 19:25:43 +03:00
|
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
2018-08-08 05:36:03 +03:00
|
|
|
import boto3
|
|
|
|
import json
|
|
|
|
from moto import mock_s3
|
2017-08-29 21:12:12 +03:00
|
|
|
from taar.recommenders import RecommendationManager
|
2018-08-14 19:34:08 +03:00
|
|
|
from taar.recommenders.recommendation_manager import TEST_CLIENT_IDS
|
|
|
|
from taar.recommenders.recommendation_manager import EMPTY_TEST_CLIENT_IDS
|
2018-07-25 04:53:38 +03:00
|
|
|
from taar.schema import INTERVENTION_A
|
2018-08-08 18:29:04 +03:00
|
|
|
from taar.schema import INTERVENTION_B
|
2018-08-13 20:45:46 +03:00
|
|
|
from taar.schema import INTERVENTION_CONTROL
|
2018-02-27 07:28:47 +03:00
|
|
|
from taar.recommenders.base_recommender import AbstractRecommender
|
2018-11-27 20:36:09 +03:00
|
|
|
|
|
|
|
from taar.recommenders.ensemble_recommender import (
|
|
|
|
TAAR_ENSEMBLE_BUCKET,
|
|
|
|
TAAR_ENSEMBLE_KEY,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2018-08-14 19:34:08 +03:00
|
|
|
from .mocks import MockRecommenderFactory
|
2018-08-08 18:29:04 +03:00
|
|
|
from .test_hybrid_recommender import install_mock_curated_data
|
2018-07-25 04:53:38 +03:00
|
|
|
|
2017-08-29 21:12:12 +03:00
|
|
|
|
2018-02-27 07:28:47 +03:00
|
|
|
class StubRecommender(AbstractRecommender):
|
2017-09-06 11:29:38 +03:00
|
|
|
""" A shared, stub recommender that can be used for testing.
|
|
|
|
"""
|
2018-11-27 20:36:09 +03:00
|
|
|
|
2017-09-06 11:29:38 +03:00
|
|
|
def __init__(self, can_recommend, stub_recommendations):
|
|
|
|
self._can_recommend = can_recommend
|
|
|
|
self._recommendations = stub_recommendations
|
|
|
|
|
2017-10-31 12:43:52 +03:00
|
|
|
def can_recommend(self, client_info, extra_data={}):
|
2017-09-06 11:29:38 +03:00
|
|
|
return self._can_recommend
|
|
|
|
|
2017-10-31 12:43:52 +03:00
|
|
|
def recommend(self, client_data, limit, extra_data={}):
|
2017-09-06 11:29:38 +03:00
|
|
|
return self._recommendations
|
|
|
|
|
|
|
|
|
2018-08-08 05:36:03 +03:00
|
|
|
def install_mocks(ctx):
|
|
|
|
ctx = ctx.child()
|
2018-08-14 18:37:37 +03:00
|
|
|
|
|
|
|
class MockProfileFetcher:
|
|
|
|
def get(self, client_id):
|
2018-11-27 20:36:09 +03:00
|
|
|
return {"client_id": client_id}
|
2018-08-08 05:36:03 +03:00
|
|
|
|
2018-11-27 20:36:09 +03:00
|
|
|
ctx["profile_fetcher"] = MockProfileFetcher()
|
|
|
|
ctx["recommender_factory"] = MockRecommenderFactory()
|
2018-08-08 05:36:03 +03:00
|
|
|
|
2018-11-27 20:36:09 +03:00
|
|
|
DATA = {
|
|
|
|
"ensemble_weights": {"collaborative": 1000, "similarity": 100, "locale": 10}
|
|
|
|
}
|
2018-02-27 09:29:14 +03:00
|
|
|
|
2018-11-27 20:36:09 +03:00
|
|
|
conn = boto3.resource("s3", region_name="us-west-2")
|
|
|
|
conn.create_bucket(Bucket=TAAR_ENSEMBLE_BUCKET)
|
|
|
|
conn.Object(TAAR_ENSEMBLE_BUCKET, TAAR_ENSEMBLE_KEY).put(Body=json.dumps(DATA))
|
2018-02-27 09:29:14 +03:00
|
|
|
|
2018-08-08 05:36:03 +03:00
|
|
|
return ctx
|
|
|
|
|
|
|
|
|
|
|
|
@mock_s3
|
|
|
|
def test_none_profile_returns_empty_list(test_ctx):
|
|
|
|
ctx = install_mocks(test_ctx)
|
2018-02-27 09:29:14 +03:00
|
|
|
rec_manager = RecommendationManager(ctx)
|
2017-08-29 21:12:12 +03:00
|
|
|
assert rec_manager.recommend("random-client-id", 10) == []
|
2017-09-06 11:29:38 +03:00
|
|
|
|
|
|
|
|
2018-08-13 20:45:46 +03:00
|
|
|
@mock_s3
|
|
|
|
def test_intervention_a(test_ctx):
|
|
|
|
ctx = install_mocks(test_ctx)
|
|
|
|
|
2018-11-27 20:36:09 +03:00
|
|
|
EXPECTED_RESULTS = [
|
|
|
|
("ghi", 3430.0),
|
|
|
|
("def", 3320.0),
|
|
|
|
("ijk", 3200.0),
|
|
|
|
("hij", 3100.0),
|
|
|
|
("lmn", 420.0),
|
|
|
|
("klm", 409.99999999999994),
|
|
|
|
("jkl", 400.0),
|
|
|
|
("abc", 23.0),
|
|
|
|
("fgh", 22.0),
|
|
|
|
("efg", 21.0),
|
|
|
|
]
|
2018-08-13 20:45:46 +03:00
|
|
|
|
|
|
|
manager = RecommendationManager(ctx.child())
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
"some_ignored_id", 10, extra_data={"branch": INTERVENTION_A}
|
|
|
|
)
|
2018-08-13 20:45:46 +03:00
|
|
|
|
|
|
|
assert isinstance(recommendation_list, list)
|
|
|
|
assert recommendation_list == EXPECTED_RESULTS
|
|
|
|
|
|
|
|
|
2018-08-08 05:36:03 +03:00
|
|
|
@mock_s3
|
2018-08-08 18:29:04 +03:00
|
|
|
def test_intervention_b(test_ctx):
|
2018-01-12 20:22:25 +03:00
|
|
|
"""The recommendation manager is currently very naive and just
|
|
|
|
selects the first recommender which returns 'True' to
|
|
|
|
can_recommend()."""
|
2018-02-27 09:29:14 +03:00
|
|
|
|
2018-08-08 18:29:04 +03:00
|
|
|
ctx = install_mocks(test_ctx)
|
|
|
|
ctx = install_mock_curated_data(ctx)
|
|
|
|
|
|
|
|
manager = RecommendationManager(ctx.child())
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
"some_ignored_id", 4, extra_data={"branch": INTERVENTION_B}
|
|
|
|
)
|
2018-08-08 18:29:04 +03:00
|
|
|
|
|
|
|
assert isinstance(recommendation_list, list)
|
|
|
|
assert len(recommendation_list) == 4
|
|
|
|
|
2018-02-22 07:50:58 +03:00
|
|
|
|
2018-08-08 05:36:03 +03:00
|
|
|
@mock_s3
|
2018-08-13 20:45:46 +03:00
|
|
|
def test_intervention_control(test_ctx):
|
2018-08-08 05:36:03 +03:00
|
|
|
ctx = install_mocks(test_ctx)
|
2018-08-13 20:45:46 +03:00
|
|
|
ctx = install_mock_curated_data(ctx)
|
2018-02-22 07:50:58 +03:00
|
|
|
|
2018-02-27 09:29:14 +03:00
|
|
|
manager = RecommendationManager(ctx.child())
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
"some_ignored_id", 10, extra_data={"branch": INTERVENTION_CONTROL}
|
|
|
|
)
|
2018-08-13 20:45:46 +03:00
|
|
|
|
|
|
|
assert len(recommendation_list) == 0
|
|
|
|
|
|
|
|
|
2018-08-14 19:34:08 +03:00
|
|
|
@mock_s3
|
|
|
|
def test_fixed_client_id_valid(test_ctx):
|
|
|
|
ctx = install_mocks(test_ctx)
|
|
|
|
ctx = install_mock_curated_data(ctx)
|
|
|
|
|
|
|
|
manager = RecommendationManager(ctx.child())
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
TEST_CLIENT_IDS[0], 10, extra_data={"branch": INTERVENTION_A}
|
|
|
|
)
|
2018-08-14 19:34:08 +03:00
|
|
|
|
|
|
|
assert len(recommendation_list) == 10
|
|
|
|
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
TEST_CLIENT_IDS[0], 10, extra_data={"branch": INTERVENTION_B}
|
|
|
|
)
|
2018-08-14 19:34:08 +03:00
|
|
|
|
|
|
|
assert len(recommendation_list) == 10
|
|
|
|
|
2018-08-13 20:45:46 +03:00
|
|
|
|
2018-08-15 00:00:58 +03:00
|
|
|
@mock_s3
|
|
|
|
def test_intervention_names(test_ctx):
|
|
|
|
# verify that intervention names can use hyphens instead of
|
|
|
|
# underscore
|
|
|
|
ctx = install_mocks(test_ctx)
|
|
|
|
ctx = install_mock_curated_data(ctx)
|
|
|
|
|
|
|
|
manager = RecommendationManager(ctx.child())
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
TEST_CLIENT_IDS[0], 10, extra_data={"branch": "intervention-a"}
|
|
|
|
)
|
2018-08-15 00:00:58 +03:00
|
|
|
|
|
|
|
assert len(recommendation_list) == 10
|
|
|
|
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
TEST_CLIENT_IDS[0], 10, extra_data={"branch": "intervention-b"}
|
|
|
|
)
|
2018-08-15 00:00:58 +03:00
|
|
|
|
|
|
|
assert len(recommendation_list) == 10
|
|
|
|
|
|
|
|
|
2018-08-14 19:34:08 +03:00
|
|
|
@mock_s3
|
|
|
|
def test_fixed_client_id_empty_list(test_ctx):
|
|
|
|
ctx = install_mocks(test_ctx)
|
|
|
|
ctx = install_mock_curated_data(ctx)
|
|
|
|
|
|
|
|
manager = RecommendationManager(ctx.child())
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
EMPTY_TEST_CLIENT_IDS[0], 10, extra_data={"branch": INTERVENTION_A}
|
|
|
|
)
|
2018-07-25 04:53:38 +03:00
|
|
|
|
2018-08-14 19:34:08 +03:00
|
|
|
assert len(recommendation_list) == 0
|
|
|
|
|
2018-11-27 20:36:09 +03:00
|
|
|
recommendation_list = manager.recommend(
|
|
|
|
EMPTY_TEST_CLIENT_IDS[0], 10, extra_data={"branch": INTERVENTION_B}
|
|
|
|
)
|
2018-08-14 19:34:08 +03:00
|
|
|
|
|
|
|
assert len(recommendation_list) == 0
|