Your IP : 216.73.217.13


Current Path : /home/deltalab/PMS/recommendations/recommender-system-batch/components/
Upload File :
Current File : //home/deltalab/PMS/recommendations/recommender-system-batch/components/vendorBased_RS.py

import json
from textwrap import indent
from _library import toolkit

from tabulate import tabulate

class vendorBased_RS:
        
    def __init__(self, products, product_identifier, filterSamePlatform):
        self.platfrom_products = products
        self.product_identifier = product_identifier
        
        self.vendor_attribute_name = 'Vendor'
        self.unknown_category_marker = ''
        
        self.filterSamePlatform = filterSamePlatform
        
        self.rs_name = 'vendorBased'
        
    def filter_similar_items(self, recommendations):
        simProduct_col_name = 'similar_products'
        similarity_threshold = 0.4
        minBundleDim = 4
            
        # A list of dictionaries. Each dictionary represents a product.
        filtered_recommendations = recommendations.copy()
      
        recommendation_names = {item['product_name']: item['item_sku'] for item in recommendations}
        
        for recommendation in recommendations:
            item_name = recommendation['product_name']
            
            if item_name in recommendation_names.keys():
                pos = list(recommendation_names.keys()).index(item_name)
            else:
                continue
        
            # Compute similarity 
            similarity_func = lambda word_to_compare: toolkit.jaccard_similarity(item_name, word_to_compare)
            similarities = dict(zip(recommendation_names.keys(), map(similarity_func, recommendation_names.keys())))
            if item_name in similarities.keys():
                similarities.pop(item_name)

            # Retrieve similar items
            similar_items = [product_name for product_name, similarity in similarities.items() 
                            if similarity >= similarity_threshold]
            
            sku_similarItems = [recommendation_names[item_name] for item_name in similar_items]
            
            if len(similar_items) > 0:
                
                itemToattach = dict(zip(sku_similarItems, similar_items))
                
                # Add the elements in the new attribute
                if simProduct_col_name in filtered_recommendations[pos].keys():
                    filtered_recommendations[pos][simProduct_col_name].update(itemToattach)
                else:
                    filtered_recommendations[pos][simProduct_col_name] = itemToattach
                    
                # Delate similar items
                for similar_item in similar_items:
                    pos = list(recommendation_names.keys()).index(similar_item)
                    
                    if len(recommendation_names) > minBundleDim:
                        recommendation_names.pop(similar_item)
                        filtered_recommendations.pop(pos)

        return filtered_recommendations
    
    def generate_recommendations(self, reference_items, filterSamePlatform, verbose = False):
        recommended_items = list()
        
        for reference_item in reference_items:
            item_name = reference_item['item_name']
            product_name = reference_item['product_name']
            
            item_vendor = reference_item['vendor']
            item_source = reference_item['inTrentino_source']
            
            # Filtering conditions
            vendorBased_cond = self.platfrom_products[self.vendor_attribute_name] == item_vendor
            sourcePlatformBased_cond = self.platfrom_products['inTrentino_source'] == item_source
            
            if filterSamePlatform:
                main_cond = vendorBased_cond & sourcePlatformBased_cond
            else: 
                main_cond = vendorBased_cond 
              
            # Try to retrieve the products
            items_vendor = self.platfrom_products[main_cond]
            
            # Drop the reference item 
            reference_item_idx = items_vendor[items_vendor[self.product_identifier] == item_name].index
            items_vendor = items_vendor.drop(index = reference_item_idx)
            
            # Compute the similarity with the reference 
            items_vendor['similarity'] = items_vendor['Title'].apply(
                lambda item_name: toolkit.jaccard_similarity(item_name, product_name))
            
            # Order according to: (1) the dissmilarity, (2) the frequency (3) the title
            items_vendor = items_vendor.drop_duplicates(subset = [self.product_identifier])
            items_vendor = items_vendor.sort_values(
                by = ['similarity', 'Frequency', 'Title'], 
                ascending = [True, False, False]).reset_index(drop = True)

            if verbose:
                print(f"\nvendorBased --> [{reference_item['item_type']}] {reference_item['product_name']} by "\
                    f"'{reference_item['vendor']}'")
                print(tabulate(items_vendor, headers=items_vendor.columns, tablefmt = 'pretty'))
                print("-" * 40)
            
            # Rename the columns 
            items_vendor = items_vendor.drop(columns = ['Type id', 'Frequency', 'similarity']) 
            items_vendor = items_vendor.rename(
                columns = {
                    'Title': 'product_name',
                    'Product Type': 'item_type', 
                    'Vendor': 'item_vendor', 
                    'SKU': 'item_sku',
                    'Seller': 'seller',
                    'production_areas': 'linked_production_area'
                    }
                )
            selected_cols = ['item_vendor', 'item_sku', 'product_name', 'item_type', 'seller', 'inTrentino_source', 'linked_production_area']
            items_vendor = items_vendor[selected_cols]

            # Create the attribute "item_name" for compatibility with the follow-up steps
            itemName_attribute = 'item_sku' if self.product_identifier.lower() == 'sku' else 'product_name'
            items_vendor['item_name'] = items_vendor[itemName_attribute]
            
            # Build the explaination
            items_vendor['explaination'] = "Ti potrebbero interessare prodotti dello stesso marchio"
            
            # Generate the ranks
            items_vendor = items_vendor.reset_index(drop = True)
            items_vendor['rank'] = [idx + 1 for idx in items_vendor.index]
            
            # Save the recommended items
            recommended_items.extend(items_vendor.to_dict(orient = 'records'))
        
        return recommended_items
        
    def itemWise_vendorBased_recommendations(self, reference_items):
        
        # Slight artefact
        if (not isinstance(reference_items, list)) or (not isinstance(reference_items, set)):
            reference_items = [reference_items]
        
        # Generate the recommendations 
        recommended_items = self.generate_recommendations(reference_items, self.filterSamePlatform)
        
        # Filter similar items
        recommended_items = self.filter_similar_items(recommended_items)
        
        # Add the name of this method to the recommendations
        #for recommendations in recommendedItems_byCategory.values():
        recommended_items = toolkit.add_recommendationSource(recommended_items, self.rs_name)

        return recommended_items
        
    def generate_vendorBased_recommendations(self):
    
        # Pre-processing the products
        reference_products = self.platfrom_products.apply(
            func = lambda df_row: toolkit.extract_referenceProduct(df_row, self.product_identifier), 
            axis = 1)
        
        # Compute the recommendation for each products
        recommendations = reference_products.apply(
            func = lambda product: self.itemWise_vendorBased_recommendations(product))
 
        # Improve the data representation
        recommendations.index = reference_products.apply(lambda product: product['item_name'])
        recommendations = recommendations.to_dict()
        
        return recommendations