Beispiel #1
0
def rerank():
    """ Re-ranks a list of items using personalized reranking """
    if request.method == 'POST':
        try:
            content = request.json
            app.logger.info(content)

            user_id = content.get('userID')
            if not user_id:
                raise BadRequest('userID is required')

            items = content.get('items')
            if not items:
                raise BadRequest('items is required')

            # Determine name of feature where reranked items are being displayed
            feature = request.args.get('feature')

            app.logger.info(items)

            # Extract item IDs from items supplied by caller. Note that unranked items
            # can be specified as a list of objects with just an 'itemId' key or as a
            # list of fully defined items/products (i.e. with an 'id' key).
            item_map = {}
            unranked_items = []
            for item in items:
                item_id = item.get('itemId') if item.get(
                    'itemId') else item.get('id')
                item_map[item_id] = item
                unranked_items.append(item_id)

            app.logger.info(unranked_items)

            ranked_items = []
            resp_headers = {}
            experiment = None

            # Get active experiment if one is setup for feature.
            if feature:
                exp_manager = ExperimentManager()
                experiment = exp_manager.get_active(feature)

            if experiment:
                app.logger.info('Using experiment: ' + experiment.name)

                # Get ranked items from experiment.
                tracker = exp_manager.default_tracker()

                ranked_items = experiment.get_items(user_id=user_id,
                                                    item_list=unranked_items,
                                                    tracker=tracker)

                resp_headers['X-Experiment-Name'] = experiment.name
                resp_headers['X-Experiment-Type'] = experiment.type
                resp_headers['X-Experiment-Id'] = experiment.id
            else:
                # Fallback to default behavior of checking for campaign ARN parameter and
                # then the default product resolver.
                values = get_parameter_values([
                    'retaildemostore-personalized-ranking-campaign-arn',
                    filter_purchased_param_name
                ])

                campaign_arn = values[0]
                filter_arn = values[1]

                if campaign_arn:
                    resolver = PersonalizeRankingResolver(
                        campaign_arn=campaign_arn, filter_arn=filter_arn)
                    resp_headers['X-Personalize-Recipe'] = get_recipe(
                        campaign_arn)
                else:
                    resolver = RankingProductsNoOpResolver()

                ranked_items = resolver.get_items(user_id=user_id,
                                                  product_list=unranked_items)

            response_items = []
            for ranked_item in ranked_items:
                item = item_map.get(ranked_item.get('itemId'))

                if 'experiment' in ranked_item and 'url' in item:
                    # Append the experiment correlation ID to the product URL so it gets tracked if used by client.
                    product_url = item.get('url')
                    if '?' in product_url:
                        product_url += '&'
                    else:
                        product_url += '?'

                    product_url += 'exp=' + ranked_item['experiment'][
                        'correlationId']

                    item['url'] = product_url

                response_items.append(item)

            resp = Response(json.dumps(response_items, cls=CompatEncoder),
                            content_type='application/json',
                            headers=resp_headers)
            return resp

        except Exception as e:
            app.logger.exception('Unexpected error reranking items', e)
            return json.dumps(items)

    if request.method == 'GET':
        app.logger.info("Request Received, Processing")
Beispiel #2
0
def get_ranking(
        user_id,
        items,
        feature,
        default_campaign_arn_param_name='retaildemostore-personalized-ranking-campaign-arn',
        top_n=None,
        context=None):
    """
    Re-ranks a list of items using personalized reranking.
    Or delegates to experiment manager if there is an active experiment.

    Args:
        user_id (int):
        items (list[dict]): e.g. [{"itemId":"33", "url":"path_to_product33"},
                                  {"itemId":"22", "url":"path_to_product22"}]
        feature: Used to lookup the currently active experiment.
        default_campaign_arn_param_name: For discounts this would be different.
        top_n (Optional[int]): Only return the top N ranked if not None.
        context (Optional[dict]): If available, passed to the reranking Personalization recipe.

    Returns:
        Items as passed in, but ordered according to reranker - also might have experimentation metadata added.
    """

    app.logger.info(f"Items given for ranking: {items}")

    # Extract item IDs from items supplied by caller. Note that unranked items
    # can be specified as a list of objects with just an 'itemId' key or as a
    # list of fully defined items/products (i.e. with an 'id' key).
    item_map = {}
    unranked_items = []
    for item in items:
        item_id = item.get('itemId') if item.get('itemId') else item.get('id')
        item_map[item_id] = item
        unranked_items.append(item_id)

    app.logger.info(f"Unranked items: {unranked_items}")

    resp_headers = {}
    experiment = None
    exp_manager = None

    # Get active experiment if one is setup for feature.
    if feature:
        exp_manager = ExperimentManager()
        experiment = exp_manager.get_active(feature)

    if experiment:
        app.logger.info('Using experiment: ' + experiment.name)

        # Get ranked items from experiment.
        tracker = exp_manager.default_tracker()

        ranked_items = experiment.get_items(user_id=user_id,
                                            item_list=unranked_items,
                                            tracker=tracker,
                                            context=context)

        app.logger.debug(
            f"Experiment ranking resolver gave us this ranking: {ranked_items}"
        )

        resp_headers['X-Experiment-Name'] = experiment.name
        resp_headers['X-Experiment-Type'] = experiment.type
        resp_headers['X-Experiment-Id'] = experiment.id
    else:
        # Fallback to default behavior of checking for campaign ARN parameter and
        # then the default product resolver.
        values = get_parameter_values(
            [default_campaign_arn_param_name, filter_purchased_param_name])
        app.logger.info(f'Falling back to Personalize: {values}')

        campaign_arn = values[0]
        filter_arn = values[1]

        if campaign_arn:
            resolver = PersonalizeRankingResolver(campaign_arn=campaign_arn,
                                                  filter_arn=filter_arn)
            resp_headers['X-Personalize-Recipe'] = get_recipe(campaign_arn)
        else:
            app.logger.info(f'Falling back to No-op: {values}')
            resolver = RankingProductsNoOpResolver()

        ranked_items = resolver.get_items(user_id=user_id,
                                          product_list=unranked_items,
                                          context=context)

    response_items = []
    if top_n is not None:
        # We may not want to return them all - for example in a "pick the top N" scenario.
        ranked_items = ranked_items[:top_n]

    for ranked_item in ranked_items:
        # Unlike with /recommendations and /related we are not hitting the products API to get product info back
        # The caller may have left that info in there so in case they have we want to leave it in.
        item = item_map.get(ranked_item.get('itemId'))

        if 'experiment' in ranked_item:

            item['experiment'] = ranked_item['experiment']

            if 'url' in item:
                # Append the experiment correlation ID to the product URL so it gets tracked if used by client.
                product_url = item.get('url')
                if '?' in product_url:
                    product_url += '&'
                else:
                    product_url += '?'

                product_url += 'exp=' + ranked_item['experiment'][
                    'correlationId']

                item['url'] = product_url

        response_items.append(item)

    return response_items, resp_headers
Beispiel #3
0
def rerank():
    """ Re-ranks a list of items using personalized reranking """
    if request.method == 'POST':
        try:
            content = request.json
            app.logger.info(content)

            user_id = content.get('userID')
            if not user_id:
                raise BadRequest('userID is required')

            items = content.get('items')
            if not items:
                raise BadRequest('items is required')

            # Determine name of feature where reranked items are being displayed
            feature = request.args.get('feature')

            app.logger.info(items)

            # Extract item IDs from items supplied by caller. Note that unranked items 
            # can be specified as a list of objects with just an 'itemId' key or as a 
            # list of fully defined items/products (i.e. with an 'id' key).
            item_map = {}
            unranked_items = []
            for item in items:
                item_id = item.get('itemId') if item.get('itemId') else item.get('id')
                item_map[item_id] = item
                unranked_items.append(item_id)

            app.logger.info(unranked_items)

            ranked_items = []
            experiment = None

            # Get active experiment if one is setup for feature and we have a user.
            if feature and user_id:
                exp_manager = ExperimentManager()
                experiment = exp_manager.get_active(feature)

            if experiment:
                # Get ranked items from experiment.
                tracker = exp_manager.default_tracker()

                ranked_items = experiment.get_items(
                    user_id = user_id, 
                    item_list = unranked_items,
                    tracker = tracker
                )
            else:
                # No experiment so check if there's a ranking campaign configured.
                response = ssm.get_parameter(
                    Name='retaildemostore-personalized-ranking-campaign-arn'
                )

                if response['Parameter']['Value'] != 'NONE':
                    resolver = PersonalizeRankingResolver(campaign_arn = response['Parameter']['Value'])
                else:
                    resolver = RankingProductsNoOpResolver()

                ranked_items = resolver.get_items(
                    user_id = user_id, 
                    product_list = unranked_items
                )

            response_items = []
            for ranked_item in ranked_items:
                item = item_map.get(ranked_item.get('itemId'))

                if 'experiment' in ranked_item and 'url' in item:
                    # Append the experiment correlation ID to the product URL so it gets tracked if used by client.
                    product_url = item.get('url')
                    if '?' in product_url:
                        product_url += '&'
                    else:
                        product_url += '?'

                    product_url += 'exp=' + ranked_item['experiment']['correlationId']

                    item['url'] = product_url

                response_items.append(item)

            return json.dumps(response_items)
    
        except Exception as e:
            app.logger.exception('Unexpected error reranking items', e)
            return json.dumps(items) 

    if request.method == 'GET':
        app.logger.info("Request Received, Processing")