def test_process_does_not_modify_title_when_the_google_product_category_is_in_the_config_but_no_keywords(
            self):
        original_title = 'Some title with no target keywords in the middle'
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title': 'Some title with no target keywords in the middle',
                'googleProductCategory': 'ファッション・アクセサリー > ジュエリー > 腕時計'
            })

        optimized_data, optimization_result = self.optimizer.process(
            original_data, 'test')
        product = optimized_data['entries'][0]['product']

        self.assertEqual(original_title, product['title'])
        self.assertEqual(0, optimization_result.num_of_products_optimized)
예제 #2
0
    def test_process_sets_product_tracking_field_to_sanitized_when_title_truncated(
            self):
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title': 'a' * (title_length_optimizer._MAX_TITLE_LENGTH * 2)
            })
        tracking_field = 'customLabel4'

        with mock.patch.dict('os.environ',
                             {'PRODUCT_TRACKING_FIELD': tracking_field}):
            optimized_data, _ = self.optimizer.process(original_data)
            product = optimized_data['entries'][0]['product']

            self.assertEqual(enums.TrackingTag.SANITIZED.value,
                             product[tracking_field])
    def test_adult_optimizer_does_nothing_if_adult_tokens_in_description_but_category_is_not_adult(
            self, is_adult, test_description, test_google_product_category):
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'adult': is_adult,
                'description': test_description,
                'googleProductCategory': test_google_product_category
            })

        optimized_data, optimization_result = self.optimizer.process(
            original_data, 'test')
        product = optimized_data['entries'][0]['product']

        self.assertEqual(False, product['adult'])
        self.assertEqual(0, optimization_result.num_of_products_optimized)
예제 #4
0
    def test_set_optimization_tracking_sets_tracking_field_sanitized_optimized_when_product_sanitized_then_optimized(
            self):
        data = requests_bodies.build_request_body()
        dummy_optimizer = DummyOptimizer()
        dummy_sanitizer = DummySanitizer()
        tracking_field = 'customLabel4'

        with mock.patch.dict('os.environ',
                             {'PRODUCT_TRACKING_FIELD': tracking_field}):
            optimized_data, _ = dummy_optimizer.process(data)
            sanitized_data, _ = dummy_sanitizer.process(optimized_data)
            sanitized_product = sanitized_data['entries'][0]['product']

            self.assertNotEqual(base_optimizer.SANITIZED_AND_OPTIMIZED,
                                sanitized_product[tracking_field])
예제 #5
0
  def test_mine_and_insert_attributes_for_batch_mines_womens_from_female_product_type(
      self):
    product_data = requests_bodies.build_request_body(
        properties_to_be_updated={
            'offerId': 'product-1',
            'title': 'dummy title',
            'googleProductCategory': 'Apparel & Accessories > Shoes',
            'productTypes': ['Apparel & Accessories', 'Women\'s', 'Shoes']
        },
        properties_to_be_removed=['gender'])
    miner = attribute_miner.AttributeMiner('test', constants.DEFAULT_COUNTRY)

    mined_attributes = miner.mine_and_insert_attributes_for_batch(product_data)

    self.assertIn("Women's", mined_attributes['product-1'].get('gender'))
예제 #6
0
  def test_mine_and_insert_attributes_does_not_inserts_colors_into_color_field_if_color_field_not_empty(
      self):
    product_data = requests_bodies.build_request_body(
        properties_to_be_updated={
            'offerId': 'product-1',
            'title': 'タイトルレッド・オレンジ',
            'color': 'Red'
        })
    miner = attribute_miner.AttributeMiner(constants.LANGUAGE_CODE_JA,
                                           constants.COUNTRY_CODE_JP)

    _ = miner.mine_and_insert_attributes_for_batch(product_data)
    product = product_data['entries'][0]['product']

    self.assertIn('Red', product['color'])
예제 #7
0
  def test_mine_and_insert_attributes_inserts_gender_into_gender_field(self):
    product_data = requests_bodies.build_request_body(
        properties_to_be_updated={
            'offerId': 'product-1',
            'title': 'dummy title',
            'googleProductCategory': 'Apparel & Accessories > Shoes',
            'productTypes': ['Apparel & Accessories', 'Women\'s', 'Shoes']
        },
        properties_to_be_removed=['gender'])
    miner = attribute_miner.AttributeMiner('test', constants.DEFAULT_COUNTRY)

    _ = miner.mine_and_insert_attributes_for_batch(product_data)
    product = product_data['entries'][0]['product']

    self.assertEqual('female', product['gender'])
    def test_color_length_optimizer_removes_all_when_all_colors_longer_than_forty_chars(
            self):
        color_with_values_longer_than_forty_chars = 'BlackBlackBlackBlackBlackBlackBlackBlackBlack/BlueBlueBlueBlueBlueBlueBlueBlueBlueBlueBlue'
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'color': color_with_values_longer_than_forty_chars
            })

        optimizer = color_length_optimizer.ColorLengthOptimizer()

        optimized_data, optimization_result = optimizer.process(original_data)
        product = optimized_data['entries'][0]['product']

        self.assertEqual('', product['color'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
예제 #9
0
    def test_process_removes_false_identifier_exists_field_when_mpn_set(self):
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'identifierExists': False,
                'brand': '',
                'mpn': '12345',
                'gtin': '',
            })

        optimized_data, optimization_result = self.optimizer.process(
            original_data)
        api_response_result = optimized_data['entries'][0]['product']

        self.assertNotIn('identifierExists', api_response_result)
        self.assertEqual(1, optimization_result.num_of_products_optimized)
    def test_process_cut_product_type_when_it_has_too_many_items(self):
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'productTypes': ['a'] *
                (product_type_length_optimizer._MAX_LIST_LENGTH + 1)
            })

        optimized_data, optimization_result = self.optimizer.process(
            original_data)
        product = optimized_data['entries'][0]['product']

        expected_product_type = [
            'a'
        ] * product_type_length_optimizer._MAX_LIST_LENGTH
        self.assertEqual(expected_product_type, product['productTypes'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
    def test_invalid_chars_optimizer_sets_product_tracking_field_to_sanitized_when_invalid_char_removed(
            self):
        # Char in pos 10 is invalid
        title_with_single_invalid_char = 'Brand 150g Product Desc'
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={'title': title_with_single_invalid_char})
        optimizer = invalid_chars_optimizer.InvalidCharsOptimizer()
        tracking_field = 'customLabel4'

        with mock.patch.dict('os.environ',
                             {'PRODUCT_TRACKING_FIELD': tracking_field}):
            optimized_data, _ = optimizer.process(original_data)
            product = optimized_data['entries'][0]['product']

            self.assertEqual(enums.TrackingTag.SANITIZED.value,
                             product[tracking_field])
    def test_process_does_not_set_product_tracking_field_when_title_equals_description(
            self):
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title': 'Beauty product',
                'description': 'Beauty product'
            })
        tracking_field = 'customLabel4'

        with mock.patch.dict('os.environ',
                             {'PRODUCT_TRACKING_FIELD': tracking_field}):
            optimized_data, _ = self.optimizer.process(original_data)
            optimized_product = optimized_data['entries'][0]['product']

            self.assertEqual('', optimized_product[tracking_field])
            self.assertEqual(original_data, optimized_data)
    def test_invalid_chars_optimizer_removes_last_char_in_unicode_private_area(
            self):
        title_with_last_char_in_unicode_private_area = (
            f'Brand 150g{chr(0xF8FF)} '
            f'Product Desc')
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title': title_with_last_char_in_unicode_private_area
            })
        optimizer = invalid_chars_optimizer.InvalidCharsOptimizer()

        optimized_data, optimization_result = optimizer.process(original_data)
        product = optimized_data['entries'][0]['product']

        self.assertEqual('Brand 150g Product Desc', product['title'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
  def test_mine_and_insert_attributes_for_batch_mines_unisex_from_multiple_description_terms(
      self):
    product_data = requests_bodies.build_request_body(
        properties_to_be_updated={
            'offerId': 'product-1',
            'title': 'dummy title',
            'description': 'Men\'s and Women\'s shoes',
            'googleProductCategory': 'Apparel & Accessories > Shoes',
            'productTypes': ['Apparel & Accessories', 'Shoes']
        },
        properties_to_be_removed=['gender'])
    miner = attribute_miner.AttributeMiner('test', constants.DEFAULT_COUNTRY)

    mined_attributes = miner.mine_and_insert_attributes_for_batch(product_data)

    self.assertIn('Unisex', mined_attributes['product-1'].get('gender'))
    def test_process_moves_at_most_three_performing_keywords_to_front_of_title(
            self):
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title':
                'Some title with multiple keywords keyword2 keyword1 heavy_keyword heavy_keyword_2 in the middle',
                'googleProductCategory': 'ファッション・アクセサリー > ジュエリー > 腕時計'
            })

        optimized_data, optimization_result = self.optimizer.process(
            original_data, 'test')
        product = optimized_data['entries'][0]['product']

        expected_title = ('keyword1 keyword2 heavy_keyword_2 Some title with '
                          'multiple keywords heavy_keyword in the middle')
        self.assertEqual(expected_title, product['title'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
    def test_size_length_optimizer_sets_product_tracking_field_to_sanitized_when_invalid_size_trimmed(
            self):
        size_more_than_100_chars = ('SmallSmallSmallSmallSmallSmallSmallSmall'
                                    'SmallSmallSmallSmallSmallSmallSmallSmall'
                                    'SmallSmallSmallSmallSmallSmallSmallSmall')
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={'sizes': [size_more_than_100_chars]})
        optimizer = size_length_optimizer.SizeLengthOptimizer()
        tracking_field = 'customLabel4'

        with mock.patch.dict('os.environ',
                             {'PRODUCT_TRACKING_FIELD': tracking_field}):
            optimized_data, _ = optimizer.process(original_data)
            product = optimized_data['entries'][0]['product']

            self.assertEqual(enums.TrackingTag.SANITIZED.value,
                             product[tracking_field])
    def test_process_does_not_change_data_when_title_is_not_truncated_from_description(
            self):
        original_title = 'b'
        original_description = 'a' * (
            title_length_optimizer._MAX_TITLE_LENGTH * 2)
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title': original_title,
                'description': original_description
            })

        optimized_data, optimization_result = self.optimizer.process(
            original_data)
        product = optimized_data['entries'][0]['product']

        self.assertEqual(original_title, product['title'])
        self.assertEqual(0, optimization_result.num_of_products_optimized)
  def test_mine_and_insert_attributes_for_batch_not_contains_gender_when_gender_could_not_be_mined(
      self):
    product_data = requests_bodies.build_request_body(
        properties_to_be_updated={
            'offerId': 'product-1',
            'title': 'dummy title',
            'gender': '',
            'googleProductCategory': 'Apparel & Accessories > Shoes',
            'productTypes': ['Apparel & Accessories', 'Shoes']
        })
    miner = attribute_miner.AttributeMiner('test', constants.DEFAULT_COUNTRY)

    mined_attributes = miner.mine_and_insert_attributes_for_batch(product_data)

    self.assertNotIn('Unisex', mined_attributes['product-1'])
    self.assertNotIn("Men's", mined_attributes['product-1'])
    self.assertNotIn("Women's", mined_attributes['product-1'])
    def test_invalid_chars_optimizer_removes_multiple_invalid_chars(self):
        # Chars in pos 10 & pos 11 are invalid
        title_with_multiple_invalid_chars = 'Brand 150g Product Title'
        desc_with_multiple_invalid_chars = 'Brand 150g Product Desc'
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title': title_with_multiple_invalid_chars,
                'description': desc_with_multiple_invalid_chars
            })
        optimizer = invalid_chars_optimizer.InvalidCharsOptimizer()

        optimized_data, optimization_result = optimizer.process(original_data)
        product = optimized_data['entries'][0]['product']

        self.assertEqual('Brand 150g Product Title', product['title'])
        self.assertEqual('Brand 150g Product Desc', product['description'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
    def test_size_length_optimizer_truncates_size_when_more_than_max_chars(
            self):
        size_more_than_100_chars = ('SmallSmallSmallSmallSmallSmallSmallSmall'
                                    'SmallSmallSmallSmallSmallSmallSmallSmall'
                                    'SmallSmallSmallSmallSmallSmallSmallSmall')
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={'sizes': [size_more_than_100_chars]})
        optimizer = size_length_optimizer.SizeLengthOptimizer()

        optimized_data, optimization_result = optimizer.process(original_data)
        product = optimized_data['entries'][0]['product']

        self.assertEqual(
            'SmallSmallSmallSmallSmallSmallSmall'
            'SmallSmallSmallSmallSmallSmallSmall'
            'SmallSmallSmallSmallSmallSmall', product['sizes'][0])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
예제 #21
0
  def test_set_optimization_tracking_returns_when_tracking_field_not_set(self):
    data = requests_bodies.build_request_body()
    dummy_optimizer = DummyOptimizer()
    tracking_field = ''

    with mock.patch.dict('os.environ',
                         {'PRODUCT_TRACKING_FIELD': tracking_field}):
      optimized_data, _ = dummy_optimizer.process(data)
      optimized_product = optimized_data['entries'][0]['product']

      # Check none of the customLabel fields were set.
      # (Nothing else we can assert on here since there are no errors/
      #  log messages. It is valid for the tracking field to be blank.)
      self.assertEqual('', optimized_product['customLabel0'])
      self.assertEqual('', optimized_product['customLabel1'])
      self.assertEqual('', optimized_product['customLabel2'])
      self.assertEqual('', optimized_product['customLabel3'])
      self.assertEqual('', optimized_product['customLabel4'])
    def test_process_completely_removes_a_keyword_from_title_and_prepend_it(
            self):
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title':
                'Some title with heavy_keyword in the middle and at the end heavy_keyword',
                'googleProductCategory': 'ファッション・アクセサリー > ジュエリー > 腕時計'
            })

        optimized_data, optimization_result = self.optimizer.process(
            original_data, 'test')
        product = optimized_data['entries'][0]['product']

        expected_title = (
            'heavy_keyword Some title with in the middle and at the '
            'end')
        self.assertEqual(expected_title, product['title'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
예제 #23
0
    def test_process_sets_product_tracking_field_to_sanitized_when_invalid_identifier_exists_removed(
            self):
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'identifierExists': False,
                'brand': 'testbrand',
                'mpn': '',
                'gtin': '',
            })
        tracking_field = 'customLabel4'

        with mock.patch.dict('os.environ',
                             {'PRODUCT_TRACKING_FIELD': tracking_field}):
            optimized_data, _ = self.optimizer.process(original_data)
            optimized_product = optimized_data['entries'][0]['product']

            self.assertEqual(enums.TrackingTag.SANITIZED.value,
                             optimized_product[tracking_field])
  def test_mine_and_insert_attributes_for_batch_mines_girls_from_female_baby_gender(
      self):
    product_data = requests_bodies.build_request_body(
        properties_to_be_updated={
            'offerId':
                'product-1',
            'title':
                'dummy title',
            'gender':
                'female',
            'googleProductCategory':
                'Apparel & Accessories > Clothing > Baby & Toddler Clothing'
        })
    miner = attribute_miner.AttributeMiner('test', constants.DEFAULT_COUNTRY)

    mined_attributes = miner.mine_and_insert_attributes_for_batch(product_data)

    self.assertIn("Girl's", mined_attributes['product-1'].get('gender'))
예제 #25
0
    def test_attribute_not_appended_to_description_if_it_already_exists_in_description(
            self):
        original_description = 'Dummy Description. Cool Company.'
        brand_to_append = 'Cool Company'
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'description': original_description,
                'brand': brand_to_append
            },
            properties_to_be_removed=['color', 'sizes'])
        mined_attrs = attribute_miner.AttributeMiner(
            constants.LANGUAGE_CODE_EN,
            constants.DEFAULT_COUNTRY).mine_and_insert_attributes_for_batch(
                original_data)
        optimizer = description_optimizer.DescriptionOptimizer(mined_attrs)

        _, optimization_result = optimizer.process(original_data, 'test')

        self.assertEqual(0, optimization_result.num_of_products_optimized)
예제 #26
0
def _build_request_body(has_mpn_field: bool,
                        mpn_value: Optional[str] = None) -> Dict[str, Any]:
    """Builds a dummy request body.

  Request body includes 1 product with specific mpn value or without mpn.

  Args:
    has_mpn_field: Whether the request body should have mpn field or not.
    mpn_value: The mpn value of the product.

  Returns:
    A dummy request body including 1 product.
  """
    properties_to_be_removed = []
    if not has_mpn_field:
        properties_to_be_removed.append('mpn')
    body = requests_bodies.build_request_body({'mpn': mpn_value},
                                              properties_to_be_removed)
    return body
    def test_process_puts_the_first_part_of_description_into_title_when_title_is_truncated_from_description(
            self, suffix):
        original_description = 'a' * (
            title_length_optimizer._MAX_TITLE_LENGTH * 2)
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'title': 'a' * (title_length_optimizer._MAX_TITLE_LENGTH - 5) +
                suffix,
                'description': original_description
            })

        optimized_data, optimization_result = self.optimizer.process(
            original_data)
        product = optimized_data['entries'][0]['product']

        expected_title = (
            original_description[:title_length_optimizer._MAX_TITLE_LENGTH])
        self.assertEqual(expected_title, product['title'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
    def test_color_length_optimizer_removes_colors_until_length_is_less_than_100(
            self):
        color_with_three_len_thirty_five_colors = (
            'BlackBlackBlackBlackBlackBlackBlack'
            '/WhiteWhiteWhiteWhiteWhiteWhiteWhite'
            '/GreenGreenGreenGreenGreenGreenGreen')
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'color': color_with_three_len_thirty_five_colors
            })

        optimizer = color_length_optimizer.ColorLengthOptimizer()

        optimized_data, optimization_result = optimizer.process(original_data)
        product = optimized_data['entries'][0]['product']

        self.assertEqual(
            'BlackBlackBlackBlackBlackBlackBlack'
            '/WhiteWhiteWhiteWhiteWhiteWhiteWhite', product['color'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
예제 #29
0
    def test_sizes_appended_to_description(self):
        original_description = 'Dummy Description.'
        sizes_to_append = ['Large']
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'description': original_description,
                'sizes': sizes_to_append
            })
        mined_attrs = attribute_miner.AttributeMiner(
            constants.LANGUAGE_CODE_EN,
            constants.DEFAULT_COUNTRY).mine_and_insert_attributes_for_batch(
                original_data)
        optimizer = description_optimizer.DescriptionOptimizer(mined_attrs)

        optimized_data, optimization_result = optimizer.process(
            original_data, 'test')
        product = optimized_data['entries'][0]['product']

        self.assertIn(sizes_to_append[0], product['description'])
        self.assertEqual(1, optimization_result.num_of_products_optimized)
예제 #30
0
    def test_attribute_not_appended_to_description_it_not_enough_space(self):
        original_description = 'a' * description_optimizer._MAX_DESCRIPTION_LENGTH
        brand_to_append = 'Cool Company'
        original_data = requests_bodies.build_request_body(
            properties_to_be_updated={
                'description': original_description,
                'brand': brand_to_append
            })
        mined_attrs = attribute_miner.AttributeMiner(
            constants.LANGUAGE_CODE_EN,
            constants.DEFAULT_COUNTRY).mine_and_insert_attributes_for_batch(
                original_data)
        optimizer = description_optimizer.DescriptionOptimizer(mined_attrs)

        optimized_data, optimization_result = optimizer.process(
            original_data, 'test')
        product = optimized_data['entries'][0]['product']

        self.assertEqual(original_description, product['description'])
        self.assertEqual(0, optimization_result.num_of_products_optimized)