Your IP : 216.73.217.13


Current Path : /home/deltalab/PMS/recommendations/user_profiling/components/
Upload File :
Current File : //home/deltalab/PMS/recommendations/user_profiling/components/ProfileBased_RS.py

from collections import defaultdict
from datetime import datetime
import json
from _library.visual_utils import visualize_message, visualize_recommendations
from components.collection_analyzer import ProductionAreaCollectionAnalyzer,AttributeCollectionAnalyzer
from datetime import datetime

import numpy as np

class ProfileBased_RS:
    def __init__(self, platformProducts, platfromOrders):
        self.platformProducts = platformProducts
        self.platfromOrders = platfromOrders
        
    def _generateRecomList(self, df, rs_name):
        
        # Select relavant columns
        selected_cols = ['Vendor', 'indaco_sku', 'SKU', 'Title', 'Product Type', 'Seller', 'inTrentino_source']
        df = df[selected_cols]
         
        # Rename only relevant columns
        df = df.rename(
            columns = {
                'Title': 'product_name',
                'Product Type': 'item_type', 
                'Vendor': 'item_vendor', 
                'SKU': 'item_sku',
                'Seller': 'seller'
                }
            )
        
        # Generate the list of recommendations
        df['rs_source'] = rs_name
        recommendations = df.to_dict(orient = 'records')
        return recommendations
      
        
    def brandBased_rs(self, user_profile, verbose):
        visualize_message('[RS] Brand based')
        
        # Retrieve the user attributes
        user_brands = user_profile['brands']
        purchased_products = user_profile['unique_products']
        
        # Filter products
        product_subset = self.platformProducts[self.platformProducts['Vendor'].isin(user_brands)]
        product_subset = product_subset[~ product_subset['SKU'].isin(purchased_products)]
        if(user_profile['adult'] == 0):
            print("Filtering products to analyze (user not an adult)")
            product_subset = product_subset[product_subset['isforadult'] == False]
        
        # Generate the list of recommendations
        brandBased_recom = self._generateRecomList(df = product_subset, rs_name = 'brandBased_recommendations')
        
        if verbose:
            print(f"User brands ({len(user_brands)}):", ' | '.join(user_brands))
            print(f"Purchased products ({len(purchased_products)}):", ' | '.join(purchased_products))
            print(product_subset)
        
        return brandBased_recom
    
    def categoryBased_rs(self, user_profile, verbose):
        visualize_message('[RS] Category based')
        
        # Retrieve the user attributes
        user_categories = user_profile['categories']
        purchased_products = user_profile['unique_products']
        
        # Filter products
        product_subset = self.platformProducts[self.platformProducts['Product Type'].isin(user_categories)]
        product_subset = product_subset[~ product_subset['SKU'].isin(purchased_products)]
        
        if(user_profile['adult'] == 0):
            print("Filtering products to analyze (user not an adult)")
            product_subset = product_subset[product_subset['isforadult'] == False]
        
        # Generate the list of recommendations
        categoryBased_recom = self._generateRecomList(df = product_subset, rs_name = 'categoryBased_recommendations')
        
        if verbose:
            print(f"User categories ({len(user_categories)}):", ' | '.join(user_categories))
            print('-' * 90)
            print(f"Purchased products ({len(purchased_products)}):\n-->", '\n--> '.join(purchased_products))
            print('-' * 90)
            print(product_subset)
        
        return categoryBased_recom
    
    def attributeBased_rs(self,user_profile, limit_bundleDim, verbose,capacity_dictionary_name):
        visualize_message('[RS] Attribute Based')
        
        # Analyze the collections
        collectionAnalyzer = AttributeCollectionAnalyzer("products", self.platformProducts, self.platfromOrders, user_profile, limit_bundleDim,capacity_dictionary_name)
        collection_score, selectedCollection_items, selectedCollection_explaination = collectionAnalyzer.analyze_collections(verbose)
        
        # Generate recommendations
        recommended_products = self.platformProducts[self.platformProducts['SKU'].isin(selectedCollection_items)].copy()
        recommended_products['rank'] = recommended_products['SKU'].apply(lambda sku: selectedCollection_items.index(sku) + 1)
        rs_name = "veganBased_recommendations"
        if(capacity_dictionary_name == "biodynamic_food_recom"):
            rs_name = "biodinamicBased_recommendations"
        elif(capacity_dictionary_name == "bio_food_recom"):
            rs_name = "bioBased_recommendations"
        elif(capacity_dictionary_name == "glutenfree_food_recom"):
            rs_name = "glutenfreeBased_recommendations"


        recommended_products = self._generateRecomList(df = recommended_products, rs_name = rs_name)
                
        return recommended_products, selectedCollection_explaination
    
    def productionAreaBased_rs(self, user_profile, limit_bundleDim, verbose): #inerire parametro attributi
        visualize_message('[RS] Production Area')
        
        # retrieve the production areas of the user
        user_prodAreas = user_profile['production_areas']
        
        if len(user_prodAreas) == 0:
            print("\t\t\t\tNo production areas linked\n")
            return [], '', ''
        
        # Analyze the products of the collections
        ranked_productionAreas = dict()
        for idk, production_area in enumerate(user_prodAreas):
            
            if verbose:
                print(f"\nProduction area ({idk + 1}/{len(user_prodAreas)}):", production_area)
            
            # Retrieve the collection items
            cond = self.platformProducts['production_areas'] == production_area
            collection_items = self.platformProducts[cond]
            
            # Analyze the collections
            collectionAnalyzer = ProductionAreaCollectionAnalyzer(production_area, collection_items, self.platfromOrders, user_profile, limit_bundleDim,"production_area")
            collection_score, selectedCollection_items, selectedCollection_explaination = collectionAnalyzer.analyze_collections(verbose)
            
            # Save the information concerning this collection
            collection_info = {
                'collection_score': collection_score, 
                'collection_items': selectedCollection_items, 
                'explaination': selectedCollection_explaination}
            ranked_productionAreas[production_area] = collection_info
 
        # Sort the production areas according to its scores
        ranked_productionAreas = dict(sorted(ranked_productionAreas.items(), 
                                             key = lambda dict_item: dict_item[1]['collection_score'],
                                             reverse = True))
        if verbose:
            print("\nUSER COLLECTION:", json.dumps(ranked_productionAreas, indent = 4))
        
        # Select one production area and its products
        selected_productionArea = list(ranked_productionAreas.keys())[0]
        
        selected_products = ranked_productionAreas[selected_productionArea]['collection_items']
        explaination = ranked_productionAreas[selected_productionArea]['explaination']
        
        # Generate recommendations
        recommended_products = self.platformProducts[self.platformProducts['SKU'].isin(selected_products)].copy()
        recommended_products['rank'] = recommended_products['SKU'].apply(lambda sku: selected_products.index(sku) + 1)
        
        recommended_products = self._generateRecomList(df = recommended_products, rs_name = 'productionAreaBased_recommendations')
                
        return recommended_products, selected_productionArea, explaination
    
    def generateBundle(self, recommendationsByMethod):
        visualize_message('Generate the bundle')
        
        # Count the items
        all_recommendations, all_productNames = list(), list()
        
        for recommendations in recommendationsByMethod.values():
            
            if isinstance(recommendations, dict):
                recommendations = recommendations['recommendations']               
                 
            # Save the recommendations
            all_recommendations.extend(recommendations)
            
            # Save the recommendation product names
            product_names = [recom['product_name'] for recom in recommendations]
            all_productNames.extend(product_names)
            
        # Count the items
        unique_products, counter = np.unique(all_productNames, return_counts = True)
        unique_products = dict(zip(unique_products, counter))        
        
        # Sort the recommendations according to: (A) the item frequency (B) the rs method
        rs_methods = list(reversed(recommendationsByMethod.keys()))
        all_recommendations = sorted(all_recommendations, 
                                     key = lambda recom: (
                                         unique_products[recom['product_name']],
                                         rs_methods.index(recom['rs_source'])),
                                     reverse = True)
        
        # Drop duplicated
        filtered_recommendations = []
        for recom in all_recommendations:
            already_recom = [recom['product_name'] for recom in filtered_recommendations]
            
            if recom['product_name'] not in already_recom:
                recom['rank'] = len(filtered_recommendations) + 1
                filtered_recommendations.append(recom)
                
        filtered_recommendations = sorted(filtered_recommendations, key = lambda recom: recom['rank'])
        
        return filtered_recommendations
    
    def userWise_recommendations(self, user_id, user_profile, collectionBased_bundleDim, verbose):
        visualize_message(f'USER ID: {user_id}', width = 110)
        
        # Initialize
        explaination = ""
        users_recommendations = defaultdict(dict)
        
        start_time = datetime.now()
         
        # ------------------------------- METHOD 1: Production Areas ---------------------------------
        productionAreaBased_recom, selected_productionArea, explaination= self.productionAreaBased_rs(user_profile, collectionBased_bundleDim, verbose)
        
        users_recommendations['productionAreaBased_recommendations']['recommendations'] = productionAreaBased_recom
        if len(productionAreaBased_recom) > 0:
            users_recommendations['productionAreaBased_recommendations']['production_area'] = selected_productionArea
            users_recommendations['productionAreaBased_recommendations']['explaination'] = explaination
            
        print(f"[Production area based] Recommendations:", len(productionAreaBased_recom))
        # -----------------------------------------------------------------------------------
        
        # ------------------------------- METHOD 2: Brands ---------------------------------
        brandBased_recom = self.brandBased_rs(user_profile, verbose)
        users_recommendations['brandBased_recommendations']['recommendations']  = brandBased_recom
        if len(brandBased_recom) > 0:
            # explaination =  "Ti potrebbero interessare altri prodotti dei marchi precedentemente acquistati"
            explaination = "I marchi che hai acquistato precedentemente propongono anche"
        users_recommendations['brandBased_recommendations']['explaination'] = explaination
        print(f"[Brand based] Recommendations:", len(brandBased_recom)) 
        # -----------------------------------------------------------------------------------
        
        # ------------------------------- METHOD 3: Categories ---------------------------------
        categoryBased_recom = self.categoryBased_rs(user_profile, verbose)
        users_recommendations['categoryBased_recommendations']['recommendations']  = categoryBased_recom
        if len(categoryBased_recom) > 0:
            explaination =  "Ti potrebbero interessare altri prodotti delle categorie acquistate precedentemente"
        users_recommendations['categoryBased_recommendations']['explaination'] = explaination
        print(f"[Category based] Recommendations:", len(categoryBased_recom))
        # -----------------------------------------------------------------------------------
        
        # ------------------------------- METHOD 4: Attribute Based Products ---------------------------------
        attributeBased_recom,explaination = [], ''
 
        if(user_profile['bio_percentage'] > 0.2): 

            attributeBased_recom, explaination= self.attributeBased_rs(user_profile, 2, verbose,"bio_food_recom")
           
            users_recommendations['bioBased_recommendations']['recommendations'] = attributeBased_recom

            if len(attributeBased_recom) > 0:
                users_recommendations['bioBased_recommendations']['explaination'] = explaination
            else:
                users_recommendations['bioBased_recommendations']['explaination'] = ""
        
        if(user_profile['biodinamic_percentage'] > 0.2): 

            attributeBased_recom, explaination= self.attributeBased_rs(user_profile, 2, verbose,"biodynamic_food_recom")
           
            users_recommendations['biodinamicBased_recommendations']['recommendations'] = attributeBased_recom

            if len(attributeBased_recom) > 0:
                users_recommendations['biodinamicBased_recommendations']['explaination'] = explaination
            else:
                users_recommendations['biodinamicBased_recommendations']['explaination'] = ""
        
        if(user_profile['vegan_percentage'] > 0.2): 

            attributeBased_recom, explaination= self.attributeBased_rs(user_profile, 2, verbose,"vegan_food_recom")
            users_recommendations['veganBased_recommendations']['recommendations'] = attributeBased_recom

            if len(attributeBased_recom) > 0:
                users_recommendations['veganBased_recommendations']['explaination'] = explaination
            else:
                users_recommendations['veganBased_recommendations']['explaination'] = ""
        
        if(user_profile['gluten_free_percentage'] > 0.2): 

            attributeBased_recom, explaination= self.attributeBased_rs(user_profile, 2, verbose,"glutenfree_food_recom")
           
            users_recommendations['glutenfreeBased_recommendations']['recommendations'] = attributeBased_recom

            if len(attributeBased_recom) > 0:
                users_recommendations['glutenfreeBased_recommendations']['explaination'] = explaination
            else:
                users_recommendations['glutenfreeBased_recommendations']['explaination'] = ""
        
        # ------------------------------- Bundle with unique products ---------------------------------
        user_bundle = self.generateBundle(users_recommendations)
        users_recommendations['recommendation_bundle']['recommendations'] = user_bundle
        if len(user_bundle) > 0:
            explaination = "Ti potrebbero interessare"
        users_recommendations['recommendation_bundle']['explaination'] = explaination
        print(f"[Bundle] Recommendations:", len(user_bundle))
        # -----------------------------------------------------------------------------------
        
         # Computation time
        raw_duration = datetime.now() - start_time
        
        temporal_unit = lambda total_seconds: 'ms' if total_seconds < 1 else 's' if total_seconds < 60 else 'm'
        user_computationalTime = np.timedelta64(raw_duration, temporal_unit(raw_duration.total_seconds()))
        print('\n\nUser duration:', user_computationalTime, "\n\n")
        
        if verbose:
            visualize_recommendations(user_bundle)
        
        # Save the computed recommendations
        return users_recommendations, user_computationalTime
    
    def userWise_recommendations_multiProcess(self, queue, user_id, user_profile, verbose):
        recommendations, computationalTime = self.userWise_recommendations(
            user_id, user_profile, verbose)
        processOutput = ({user_id: recommendations}, 
                         {user_id: computationalTime})
        queue.put(processOutput)