From 44363b91795dd1ebc413ec5f14199bbd6869d183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Tesa=CC=81rek?= Date: Fri, 20 Mar 2020 11:01:57 +0100 Subject: [PATCH 1/2] Implementation of boosting rules (#109) for Matej9 --- README.md | 9 ++++ src/Model/Command/Boost.php | 53 +++++++++++++++++++ src/Model/Command/UserRecommendation.php | 37 +++++++++++++ .../RecommendationRequestBuilderTest.php | 6 ++- tests/unit/Model/Command/BoostTest.php | 31 +++++++++++ .../Model/Command/UserRecommendationTest.php | 37 ++++++++++++- 6 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 src/Model/Command/Boost.php create mode 100644 tests/unit/Model/Command/BoostTest.php diff --git a/README.md b/README.md index b620d22..1b09ead 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,15 @@ $recommendations = $response->getRecommendation()->getData(); // } ``` +You can further modify which items will be reccomended by providing boosting rules. Priority of items matching the +MQL `$criteria` will be multiplied by the value of `multiplier`: + +```php +$reccomendation = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600) + ->addBoost(Boost::create('valid_to >= NOW()', 2)) + ->addBoost(Boost::create('for_recommendation = 1', 3.5)) +``` + #### Recommendation response properties Every item in Matej has its id, and optionally other item properties. These properties can be set up in [item properties setup](#item-properties-setup-to-setup-you-matej-database), diff --git a/src/Model/Command/Boost.php b/src/Model/Command/Boost.php new file mode 100644 index 0000000..f084269 --- /dev/null +++ b/src/Model/Command/Boost.php @@ -0,0 +1,53 @@ +setQuery($query); + $this->setMultiplier($multiplier); + } + + /** + * Create boost rule to prioritize items + * + * @return static + */ + public static function create(string $query, float $multiplier): self + { + return new static($query, $multiplier); + } + + public function setQuery(string $query): void + { + $this->query = $query; + } + + public function setMultiplier(float $multiplier): void + { + Assertion::greaterThan($multiplier, 0); + + $this->multiplier = $multiplier; + } + + public function jsonSerialize(): array + { + return [ + 'query' => $this->query, + 'multiplier' => $this->multiplier, + ]; + } +} diff --git a/src/Model/Command/UserRecommendation.php b/src/Model/Command/UserRecommendation.php index c900364..7e3c0b4 100644 --- a/src/Model/Command/UserRecommendation.php +++ b/src/Model/Command/UserRecommendation.php @@ -38,6 +38,8 @@ class UserRecommendation extends AbstractCommand implements UserAwareInterface private $responseProperties = []; /** @var bool */ private $allowSeen = false; + /** @var Boost[] */ + private $boosts = []; private function __construct( string $userId, @@ -189,6 +191,30 @@ public function setAllowSeen(bool $seen): self return $this; } + /** + * Add a boost rule to already added rules. + * + * @return $this + */ + public function addBoost(Boost $boost): self + { + $this->boosts[] = $boost; + + return $this; + } + + /** + * Set boosts. Removes all previously set rules. + * + * @return $this + */ + public function setBoosts(array $boosts): self + { + $this->boosts = $boosts; + + return $this; + } + public function getUserId(): string { return $this->userId; @@ -239,6 +265,13 @@ protected function getCommandType(): string return 'user-based-recommendations'; } + protected function serializeBoosts(): array + { + return array_map( + function($boost) {return $boost->jsonSerialize(); }, $this->boosts + ); + } + protected function getCommandParameters(): array { $parameters = [ @@ -262,6 +295,10 @@ protected function getCommandParameters(): array $parameters['allow_seen'] = $this->allowSeen; } + if (!empty($this->boosts)) { + $parameters['boost_rules'] = $this->serializeBoosts(); + } + return $parameters; } } diff --git a/tests/integration/RequestBuilder/RecommendationRequestBuilderTest.php b/tests/integration/RequestBuilder/RecommendationRequestBuilderTest.php index 55e7d8f..4b3d5a2 100644 --- a/tests/integration/RequestBuilder/RecommendationRequestBuilderTest.php +++ b/tests/integration/RequestBuilder/RecommendationRequestBuilderTest.php @@ -3,6 +3,7 @@ namespace Lmc\Matej\IntegrationTests\RequestBuilder; use Lmc\Matej\IntegrationTests\IntegrationTestCase; +use Lmc\Matej\Model\Command\Boost; use Lmc\Matej\Model\Command\Interaction; use Lmc\Matej\Model\Command\UserMerge; use Lmc\Matej\Model\Command\UserRecommendation; @@ -20,8 +21,9 @@ public function shouldExecuteRecommendationRequestOnly(): void { $response = static::createMatejInstance() ->request() - ->recommendation($this->createRecommendationCommand('user-a')) - ->send(); + ->recommendation($this->createRecommendationCommand('user-a') + ->addBoost(Boost::create('test', 1.2)) + )->send(); $this->assertInstanceOf(RecommendationsResponse::class, $response); $this->assertResponseCommandStatuses($response, 'SKIPPED', 'SKIPPED', 'OK'); diff --git a/tests/unit/Model/Command/BoostTest.php b/tests/unit/Model/Command/BoostTest.php new file mode 100644 index 0000000..b0c2474 --- /dev/null +++ b/tests/unit/Model/Command/BoostTest.php @@ -0,0 +1,31 @@ += NOW()', 2.1); + $this->assertSame( + ['query' => 'valid_to >= NOW()', 'multiplier' => 2.1], + $boost->jsonSerialize() + ); + } + + /** + * @test + */ + public function multiplierHasToBeGreaterThan0(): void + { + $this->expectException(DomainException::class); + $this->expectExceptionMessage('Provided "-1" is not greater than "0".'); + $boost = Boost::create('valid_to >= NOW()', -1); + } +} diff --git a/tests/unit/Model/Command/UserRecommendationTest.php b/tests/unit/Model/Command/UserRecommendationTest.php index 6c87949..6a7ef05 100644 --- a/tests/unit/Model/Command/UserRecommendationTest.php +++ b/tests/unit/Model/Command/UserRecommendationTest.php @@ -52,7 +52,9 @@ public function shouldUseCustomParameters(): void ->enableHardRotation() ->setFilters(['foo = bar', 'baz = ban']) ->setModelName($modelName) - ->setAllowSeen(true); + ->setAllowSeen(true) + ->addBoost(Boost::create('valid_to >= NOW()', 1.0)) + ->addBoost(Boost::create('custom = argument', 2.0)); $this->assertInstanceOf(UserRecommendation::class, $command); $this->assertSame( @@ -71,6 +73,10 @@ public function shouldUseCustomParameters(): void 'properties' => [], 'model_name' => $modelName, 'allow_seen' => true, + 'boost_rules' => [ + ['query' => 'valid_to >= NOW()', 'multiplier' => 1.0], + ['query' => 'custom = argument', 'multiplier' => 2.0], + ], ], ], $command->jsonSerialize() @@ -124,4 +130,33 @@ public function shouldAllowModificationOfResponseProperties(): void $command->setResponseProperties(['position_title']); $this->assertSame(['position_title'], $command->jsonSerialize()['parameters']['properties']); } + + /** @test */ + public function shouldResetBoostRules(): void + { + $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600) + ->addBoost(Boost::create('valid_to >= NOW()', 1.0)); + + $command->setBoosts([ + Boost::create('foo = bar', 1.2), + Boost::create('baz = ban', 3.4), + ]); + + $this->assertSame( + [ + ['query' => 'foo = bar', 'multiplier' => 1.2], + ['query' => 'baz = ban', 'multiplier' => 3.4], + ], + $command->jsonSerialize()['parameters']['boost_rules'] + ); + } + + /** @test */ + public function shouldNotIncludeEmptyBoosts(): void + { + $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600) + ->setBoosts([]); + + $this->assertArrayNotHasKey('boost_rules', $command->jsonSerialize()['parameters']); + } } From 97616d65106790fd2aa53e2bb9485f3e99d746de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Tesa=CC=81rek?= Date: Sun, 22 Mar 2020 21:36:25 +0100 Subject: [PATCH 2/2] UserReccomendation requires only user_id and scenario arguments (#108) --- README.md | 35 +++++--- UPGRADE-3.0.md | 32 +++++++ src/Model/Command/UserRecommendation.php | 88 +++++++++++-------- .../CampaignRequestBuilderTest.php | 11 +-- .../RecommendationRequestBuilderTest.php | 11 +-- .../Model/Command/UserRecommendationTest.php | 25 +++--- .../CampaignRequestBuilderTest.php | 32 +++++-- .../RecommendationRequestBuilderTest.php | 30 +++++-- .../RequestBuilderFactoryTest.php | 5 +- 9 files changed, 182 insertions(+), 87 deletions(-) create mode 100644 UPGRADE-3.0.md diff --git a/README.md b/README.md index 1b09ead..bf60ae0 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ when providing recommendations. $matej = new Matej('accountId', 'apikey'); $response = $matej->request() - ->recommendation(UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600)) + ->recommendation(UserRecommendation::create('user-id', 'test-scenario')) ->setInteraction(Interaction::purchase('user-id', 'item-id')) // optional ->setUserMerge(UserMerge::mergeInto('user-id', 'source-id')) // optional ->send(); @@ -193,8 +193,11 @@ $recommendations = $response->getRecommendation()->getData(); You can also set more granular options of the recommendation command: ```php -$recommendation = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600); -$recommendation->setFilters(['for_recommendation = 1']) +$recommendation = UserRecommendation::create('user-id', 'test-scenario') + ->setCount(5) + ->setRotationRate(1.0) + ->setRotationTime(3600) + ->setFilters(['for_recommendation = 1']) ->setMinimalRelevance(MinimalRelevance::HIGH()) ->enableHardRotation(); @@ -231,9 +234,9 @@ You can further modify which items will be reccomended by providing boosting rul MQL `$criteria` will be multiplied by the value of `multiplier`: ```php -$reccomendation = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600) +$reccomendation = UserRecommendation::create('user-id', 'test-scenario') ->addBoost(Boost::create('valid_to >= NOW()', 2)) - ->addBoost(Boost::create('for_recommendation = 1', 3.5)) + ->addBoost(Boost::create('for_recommendation = 1', 3.5)); ``` #### Recommendation response properties @@ -242,12 +245,11 @@ Every item in Matej has its id, and optionally other item properties. These prop and you can upload item data in the [events](#send-events-data-to-matej) request. This has major benefit because you can request these properties to be returned as part of your Recommendation Request. -We call them response properties, and they can be specified either as the last parameter of `UserRecommendation::create` function, -by calling `->addResponseProperty()` method, or by calling `->setResponseProperties()` method. Following will request an `item_id`, +We call them response properties. They can be specified by calling `->addResponseProperty()` method or by calling `->setResponseProperties()` method. Following will request an `item_id`, `item_url` and `item_title`: ```php -$recommendation = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600, ['item_url']) +$recommendation = UserRecommendation::create('user-id', 'test-scenario') ->addResponseProperty('item_title'); $response = $matej->request() @@ -327,8 +329,12 @@ $response = $matej->request() ->addSorting(Sorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3'])) ->addSortings([/* array of Sorting objects */]) // Request user-based recommendations - ->addRecommendation(UserRecommendation::create('user-id', 10, 'emailing', 1.0, 3600)) - ->addRecommendations([/* array of UserRecommendation objects */]) + ->addRecommendation( + UserRecommendation::create('user-id', 'emailing') + ->setCount(10) + ->setRotationRate(1.0) + ->setRotationTime(3600) + )->addRecommendations([/* array of UserRecommendation objects */]) ->send(); ``` @@ -339,8 +345,11 @@ but once available, you can specify which model you want to use when requesting This is available for `recommendation`, `sorting` and `campaign` requests: ```php -$recommendationCommand = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600); -$recommendationCommand->setModelName('alpha'); +$recommendationCommand = UserRecommendation::create('user-id', 'test-scenario'); + ->setCount(5) + ->setRotationRate(1.0) + ->setRotationTime(3600) + ->setModelName('alpha'); $sortingCommand = Sorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3']); $sortingCommand->setModelName('beta') @@ -366,7 +375,7 @@ Typically, you'd select a random sample of users, to which you'd present recomme in your code should look similar to this: ```php -$recommendation = UserRecommendation::create('user-id', 5, 'test-scenario', 1.0, 3600); +$recommendation = UserRecommendation::create('user-id', 'test-scenario') if ($session->isUserInBucketB()) { $recommendation->setModelName('alpha'); diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md new file mode 100644 index 0000000..6e5da14 --- /dev/null +++ b/UPGRADE-3.0.md @@ -0,0 +1,32 @@ +# Upgrading from 2.x to 3.0 + +API client release 2.0 contains few backward incompatible changes. + +This guide will help you upgrade your codebase. + +## `UserRecommendation::create()` now accepts only `$user_id` and `$scenario` +`UserReccomentation::create()` accepts only two argumens: `$user_id` and `$scenario`. +Both are arguments are required. Reccomendation command can be further parametrized +using fluent API. + +#### Before +```php +$recommendation = UserRecommendation::create('user-id', 5, 'scenario', 1.0, 3600); +``` + +#### After +```php +$recommendation = UserRecommendation::create('user-id', 'scenario') + ->setCount(5) + ->setRotationRate(1.0) + ->setRotationTime(3600); +``` + +which is equivalent to + +```php +$recommendation = UserRecommendation::create('user-id', 'scenario'); +$recommendation->setCount(5); +$recommendation->setRotationRate(1.0); +$recommendation->setRotationTime(3600); +``` \ No newline at end of file diff --git a/src/Model/Command/UserRecommendation.php b/src/Model/Command/UserRecommendation.php index 7e3c0b4..5e6dca8 100644 --- a/src/Model/Command/UserRecommendation.php +++ b/src/Model/Command/UserRecommendation.php @@ -41,46 +41,22 @@ class UserRecommendation extends AbstractCommand implements UserAwareInterface /** @var Boost[] */ private $boosts = []; - private function __construct( - string $userId, - int $count, - string $scenario, - float $rotationRate, - int $rotationTime, - array $responseProperties - ) { + private function __construct(string $userId, string $scenario) + { $this->minimalRelevance = MinimalRelevance::LOW(); - $this->setUserId($userId); - $this->setCount($count); $this->setScenario($scenario); - $this->setRotationRate($rotationRate); - $this->setRotationTime($rotationTime); - $this->setResponseProperties($responseProperties); } /** * @param string $userId - * @param int $count Number of requested recommendations. The real number of recommended items could be lower or - * even zero when there are no items relevant for the user. * @param string $scenario Name of the place where recommendations are applied - eg. 'search-results-page', * 'emailing', 'empty-search-results, 'homepage', ... - * @param float $rotationRate How much should the item be penalized for being recommended again in the near future. - * Set from 0.0 for no rotation (same items will be recommended) up to 1.0 (same items should not be recommended). - * @param int $rotationTime Specify for how long will the item's rotationRate be taken in account and so the item - * is penalized for recommendations. - * @param string[] $responseProperties Specify which properties you want to retrieve from Matej alongside the item_id. * @return static */ - public static function create( - string $userId, - int $count, - string $scenario, - float $rotationRate, - int $rotationTime, - array $responseProperties = [] - ): self { - return new static($userId, $count, $scenario, $rotationRate, $rotationTime, $responseProperties); + public static function create(string $userId, string $scenario): self + { + return new static($userId, $scenario); } /** @@ -137,7 +113,11 @@ public function setFilters(array $filters): self } /** - * Add another response property you want returned. item_id is always returned by Matej. + * Add another response property you want returned. item_id is always + * returned by Matej. + * + * @param string $property + * @return $this */ public function addResponseProperty(string $property): self { @@ -227,11 +207,20 @@ protected function setUserId(string $userId): void $this->userId = $userId; } - protected function setCount(int $count): void + /** + * Set number of requested recommendations. The real number of recommended + * items could be lower or even zero when there are no items relevant for + * the user. + * + * @return $this + */ + public function setCount(int $count): self { Assertion::greaterThan($count, 0); $this->count = $count; + + return $this; } protected function setScenario(string $scenario): void @@ -241,18 +230,36 @@ protected function setScenario(string $scenario): void $this->scenario = $scenario; } - protected function setRotationRate(float $rotationRate): void + /** + * Set how much should the item be penalized for being recommended again in + * the near future. + * + * @param float $rotationRate + * @return $this + */ + public function setRotationRate(float $rotationRate): self { Assertion::between($rotationRate, 0, 1); $this->rotationRate = $rotationRate; + + return $this; } - protected function setRotationTime(int $rotationTime): void + /** + * Specify for how long will the item's rotationRate be taken in account and + * so the item is penalized for recommendations. + * + * @param int $rotationTime + * @return $this + */ + public function setRotationTime(int $rotationTime): self { Assertion::greaterOrEqualThan($rotationTime, 0); $this->rotationTime = $rotationTime; + + return $this; } protected function assembleFiltersString(): string @@ -276,10 +283,7 @@ protected function getCommandParameters(): array { $parameters = [ 'user_id' => $this->userId, - 'count' => $this->count, 'scenario' => $this->scenario, - 'rotation_rate' => $this->rotationRate, - 'rotation_time' => $this->rotationTime, 'hard_rotation' => $this->hardRotation, 'min_relevance' => $this->minimalRelevance->jsonSerialize(), 'filter' => $this->assembleFiltersString(), @@ -287,6 +291,18 @@ protected function getCommandParameters(): array 'properties' => $this->responseProperties, ]; + if ($this->count !== null) { + $parameters['count'] = $this->count; + } + + if ($this->rotationRate !== null) { + $parameters['rotation_rate'] = $this->rotationRate; + } + + if ($this->rotationRate !== null) { + $parameters['rotation_time'] = $this->rotationTime; + } + if ($this->modelName !== null) { $parameters['model_name'] = $this->modelName; } diff --git a/tests/integration/RequestBuilder/CampaignRequestBuilderTest.php b/tests/integration/RequestBuilder/CampaignRequestBuilderTest.php index 196dcef..05a1df8 100644 --- a/tests/integration/RequestBuilder/CampaignRequestBuilderTest.php +++ b/tests/integration/RequestBuilder/CampaignRequestBuilderTest.php @@ -47,13 +47,10 @@ public function shouldExecuteRecommendationAndSortingCommands(): void private function createRecommendationCommand(string $letter): UserRecommendation { - return UserRecommendation::create( - 'user-' . $letter, - 1, - 'integration-test-scenario', - 1, - 3600 - ); + return UserRecommendation::create('user-' . $letter, 'integration-test-scenario') + ->setCount(1) + ->setRotationRate(1) + ->setRotationTime(3600); } private function createSortingCommand(string $letter): Sorting diff --git a/tests/integration/RequestBuilder/RecommendationRequestBuilderTest.php b/tests/integration/RequestBuilder/RecommendationRequestBuilderTest.php index 4b3d5a2..feec868 100644 --- a/tests/integration/RequestBuilder/RecommendationRequestBuilderTest.php +++ b/tests/integration/RequestBuilder/RecommendationRequestBuilderTest.php @@ -79,13 +79,10 @@ public function shouldReturnInvalidCommandOnInvalidPropertyName(): void private function createRecommendationCommand(string $username): UserRecommendation { - return UserRecommendation::create( - $username, - 5, - 'integration-test-scenario', - 0.50, - 3600 - ); + return UserRecommendation::create($username, 'integration-test-scenario') + ->setCount(5) + ->setRotationRate(0.50) + ->setRotationTime(3600); } private function assertShorthandResponse( diff --git a/tests/unit/Model/Command/UserRecommendationTest.php b/tests/unit/Model/Command/UserRecommendationTest.php index 6a7ef05..713c25b 100644 --- a/tests/unit/Model/Command/UserRecommendationTest.php +++ b/tests/unit/Model/Command/UserRecommendationTest.php @@ -10,18 +10,15 @@ class UserRecommendationTest extends TestCase /** @test */ public function shouldBeInstantiableViaNamedConstructorWithDefaultValues(): void { - $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600); + $command = UserRecommendation::create('user-id', 'test-scenario'); $this->assertInstanceOf(UserRecommendation::class, $command); - $this->assertSame( + $this->assertEquals( [ 'type' => 'user-based-recommendations', 'parameters' => [ 'user_id' => 'user-id', - 'count' => 333, 'scenario' => 'test-scenario', - 'rotation_rate' => 1.0, - 'rotation_time' => 3600, 'hard_rotation' => false, 'min_relevance' => MinimalRelevance::LOW, 'filter' => '', @@ -46,7 +43,10 @@ public function shouldUseCustomParameters(): void $rotationTime = random_int(1, 86400); $modelName = 'test-model-' . md5(microtime()); - $command = UserRecommendation::create($userId, $count, $scenario, $rotationRate, $rotationTime); + $command = UserRecommendation::create($userId, $scenario) + ->setCount($count) + ->setRotationRate($rotationRate) + ->setRotationTime($rotationTime); $command->setMinimalRelevance(MinimalRelevance::HIGH()) ->enableHardRotation() @@ -57,7 +57,8 @@ public function shouldUseCustomParameters(): void ->addBoost(Boost::create('custom = argument', 2.0)); $this->assertInstanceOf(UserRecommendation::class, $command); - $this->assertSame( + + $this->assertEquals( [ 'type' => 'user-based-recommendations', 'parameters' => [ @@ -86,7 +87,7 @@ public function shouldUseCustomParameters(): void /** @test */ public function shouldAssembleMqlFilters(): void { - $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600); + $command = UserRecommendation::create('user-id', 'test-scenario'); // Default filter $this->assertSame('', $command->jsonSerialize()['parameters']['filter']); @@ -118,12 +119,12 @@ public function shouldAssembleMqlFilters(): void /** @test */ public function shouldAllowModificationOfResponseProperties(): void { - $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600, ['test']); + $command = UserRecommendation::create('user-id', 'test-scenario'); + $command->addResponseProperty('test'); $this->assertSame(['test'], $command->jsonSerialize()['parameters']['properties']); // Add some properties $command->addResponseProperty('url'); - $this->assertSame(['test', 'url'], $command->jsonSerialize()['parameters']['properties']); // Overwrite all properties @@ -134,7 +135,7 @@ public function shouldAllowModificationOfResponseProperties(): void /** @test */ public function shouldResetBoostRules(): void { - $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600) + $command = UserRecommendation::create('user-id', 'test-scenario') ->addBoost(Boost::create('valid_to >= NOW()', 1.0)); $command->setBoosts([ @@ -154,7 +155,7 @@ public function shouldResetBoostRules(): void /** @test */ public function shouldNotIncludeEmptyBoosts(): void { - $command = UserRecommendation::create('user-id', 333, 'test-scenario', 1.0, 3600) + $command = UserRecommendation::create('user-id', 'test-scenario') ->setBoosts([]); $this->assertArrayNotHasKey('boost_rules', $command->jsonSerialize()['parameters']); diff --git a/tests/unit/RequestBuilder/CampaignRequestBuilderTest.php b/tests/unit/RequestBuilder/CampaignRequestBuilderTest.php index e4fe063..28be568 100644 --- a/tests/unit/RequestBuilder/CampaignRequestBuilderTest.php +++ b/tests/unit/RequestBuilder/CampaignRequestBuilderTest.php @@ -23,9 +23,21 @@ public function shouldBuildRequestWithCommands(): void { $builder = new CampaignRequestBuilder(); - $recommendationCommand1 = UserRecommendation::create('userId1', 1, 'scenario1', 1.0, 600); - $recommendationCommand2 = UserRecommendation::create('userId2', 2, 'scenario2', 0.5, 700); - $recommendationCommand3 = UserRecommendation::create('userId3', 3, 'scenario3', 0.0, 800); + $recommendationCommand1 = UserRecommendation::create('userId1', 'scenario1') + ->setCount(1) + ->setRotationRate(1.0) + ->setRotationTime(600); + + $recommendationCommand2 = UserRecommendation::create('userId2', 'scenario2') + ->setCount(2) + ->setRotationRate(0.5) + ->setRotationTime(700); + + $recommendationCommand3 = UserRecommendation::create('userId3', 'scenario3') + ->setCount(3) + ->setRotationRate(0.0) + ->setRotationTime(800); + $builder->addRecommendation($recommendationCommand1); $builder->addRecommendations([$recommendationCommand2, $recommendationCommand3]); @@ -72,7 +84,12 @@ public function shouldThrowExceptionWhenBatchSizeIsTooBig(): void $builder = new CampaignRequestBuilder(); for ($i = 0; $i < 501; $i++) { - $builder->addRecommendation(UserRecommendation::create('userId1', 1, 'scenario1', 1.0, 600)); + $builder->addRecommendation( + UserRecommendation::create('userId1', 'scenario1') + ->setCount(1) + ->setRotationRate(1.0) + ->setRotationTime(600) + ); $builder->addSorting(Sorting::create('userId1', ['itemId1', 'itemId2'])); } @@ -105,7 +122,12 @@ public function shouldSendRequestViaRequestManager(): void $builder = new CampaignRequestBuilder(); $builder->setRequestManager($requestManagerMock); - $builder->addRecommendation(UserRecommendation::create('userId1', 1, 'scenario1', 1.0, 3600)); + $builder->addRecommendation( + UserRecommendation::create('userId1', 'scenario1') + ->setCount(1) + ->setRotationRate(1.0) + ->setRotationTime(600) + ); $builder->addSorting(Sorting::create('userId1', ['itemId1', 'itemId2'])); $builder->send(); diff --git a/tests/unit/RequestBuilder/RecommendationRequestBuilderTest.php b/tests/unit/RequestBuilder/RecommendationRequestBuilderTest.php index df3db33..b1094cb 100644 --- a/tests/unit/RequestBuilder/RecommendationRequestBuilderTest.php +++ b/tests/unit/RequestBuilder/RecommendationRequestBuilderTest.php @@ -23,7 +23,10 @@ class RecommendationRequestBuilderTest extends TestCase /** @test */ public function shouldBuildRequestWithCommands(): void { - $recommendationsCommand = UserRecommendation::create('userId1', 5, 'test-scenario', 0.5, 3600); + $recommendationsCommand = UserRecommendation::create('userId1', 'test-scenario') + ->setCount(5) + ->setRotationRate(0.5) + ->setRotationTime(3600); $builder = new RecommendationRequestBuilder($recommendationsCommand); $interactionCommand = Interaction::detailView('sourceId1', 'itemId1'); @@ -53,7 +56,10 @@ public function shouldBuildRequestWithCommands(): void /** @test */ public function shouldThrowExceptionWhenSendingCommandsWithoutRequestManager(): void { - $recommendationsCommand = UserRecommendation::create('userId1', 5, 'test-scenario', 0.5, 3600); + $recommendationsCommand = UserRecommendation::create('userId1', 'test-scenario') + ->setCount(5) + ->setRotationRate(0.5) + ->setRotationTime(3600); $builder = new RecommendationRequestBuilder($recommendationsCommand); $this->expectException(LogicException::class); @@ -79,7 +85,10 @@ public function shouldSendRequestViaRequestManager(): void public function shouldThrowExceptionWhenInteractionIsForUnrelatedUser(): void { $builder = new RecommendationRequestBuilder( - $recommendationsCommand = UserRecommendation::create('userId1', 5, 'scenario', 0.5, 3600) + $recommendationsCommand = UserRecommendation::create('userId1', 'scenario') + ->setCount(5) + ->setRotationRate(0.5) + ->setRotationTime(3600) ); $builder->setInteraction(Interaction::purchase('different-user', 'itemId1')); @@ -96,7 +105,10 @@ public function shouldThrowExceptionWhenInteractionIsForUnrelatedUser(): void public function shouldThrowExceptionWhenMergeIsForUnrelatedUser(): void { $builder = new RecommendationRequestBuilder( - $recommendationsCommand = UserRecommendation::create('userId1', 5, 'scenario', 0.5, 3600) + $recommendationsCommand = UserRecommendation::create('userId1', 'scenario') + ->setCount(5) + ->setRotationRate(0.5) + ->setRotationTime(3600) ); $builder->setUserMerge(UserMerge::mergeInto('different-user', 'userId1')); @@ -123,7 +135,10 @@ public function shouldPassOnCorrectSequenceOfUsersWhenMerging( ): void { $interactionCommand = Interaction::purchase($interactionUser, 'test-item-id'); $userMergeCommand = UserMerge::mergeFromSourceToTargetUser($sourceUserToBeDeleted, $targetUserId); - $recommendationsCommand = UserRecommendation::create($recommendationUser, 5, 'scenario', 0.5, 3600); + $recommendationsCommand = UserRecommendation::create($recommendationUser, 'scenario') + ->setCount(5) + ->setRotationRate(0.5) + ->setRotationTime(3600); $builder = new RecommendationRequestBuilder($recommendationsCommand); $builder->setUserMerge($userMergeCommand); @@ -140,7 +155,10 @@ public function shouldFailOnIncorrectSequenceOfUsersWhenMerging(): void { $interactionCommand = Interaction::purchase('test-user-a', 'test-item-id'); $userMergeCommand = UserMerge::mergeFromSourceToTargetUser('test-user-b', 'test-user-a'); - $recommendationsCommand = UserRecommendation::create('test-user-b', 5, 'scenario', 0.5, 3600); + $recommendationsCommand = UserRecommendation::create('test-user-b', 'scenario') + ->setCount(5) + ->setRotationRate(0.5) + ->setRotationTime(3600); $this->expectException(LogicException::class); $this->expectExceptionMessage( diff --git a/tests/unit/RequestBuilder/RequestBuilderFactoryTest.php b/tests/unit/RequestBuilder/RequestBuilderFactoryTest.php index d14d5d2..041235e 100644 --- a/tests/unit/RequestBuilder/RequestBuilderFactoryTest.php +++ b/tests/unit/RequestBuilder/RequestBuilderFactoryTest.php @@ -71,7 +71,10 @@ public function provideBuilderMethods(): array $voidInit = function ($builder): void {}; - $userRecommendation = UserRecommendation::create('user-id', 1, 'test-scenario', 0.5, 3600); + $userRecommendation = UserRecommendation::create('user-id', 'test-scenario') + ->setCount(5) + ->setRotationRate(0.5) + ->setRotationTime(3600); return [ 'getItemProperties' => ['getItemProperties', ItemPropertiesGetRequestBuilder::class, $voidInit],