Example #1
0
def get_recommendations(product_id):
    """
              Get the recommended items from the database given a product ID
          """
    product_id = product_id.replace("'", "''")
    results = database.execute_query(
        "select recommendations from simplerecs where product_id = %s",
        (product_id, ))
    recs = [
        convert_to_model.toProduct(
            database.execute_query(
                "select * from products where product_id = %s", (e, ))[0])
        for e in results[0][0]
    ]
    return recs
Example #2
0
def prioritize_discount(recs, limit):
    """Give priority to recommendations with (higher) discounts"""

    if len(recs) > 4:
        # get all product_prices data from the DB
        price_data = database.execute_query(f"select * from product_prices",
                                            "")

        # select all the product prices from the cart where the discount is not none(we exclude None so we can use sorted() later)
        product_prices = [
            product_price for product_price in price_data
            if product_price[0] in recs if product_price[1] is not None
        ]

        # randomize the placement of these prices
        product_prices = (random.sample(product_prices, len(product_prices)))

        # sort the prices on discount
        sorted_product_prices = list(
            reversed(sorted(product_prices, key=lambda x: x[1])))

        # add prices where discount is None
        sorted_product_prices = sorted_product_prices + [
            product_price for product_price in price_data
            if product_price[0] in recs if product_price[1] is None
        ]

        # select product_ids
        recs = [x[0] for x in sorted_product_prices]

        # return limit product_ids
        return recs[:limit]
    else:
        return recs
Example #3
0
def fill_table_property_matching(cursor, connection):
    """create and fill a table based on the property_matching() function"""

    cursor.execute("select count(*) from property_matching_recs")
    hasEntries = True if cursor.fetchone()[0] > 0 else False

    if not hasEntries:

        cursor.execute("select product_id from products")
        data = cursor.fetchall()

        price_data = database.execute_query(
            f"""select pc.product_id , doelgroep , eenheid, factor, gebruik, geschiktvoor, geursoort, huidconditie,huidtype , huidtypegezicht , inhoud , klacht , kleur , leeftijd , serie, soort,soorthaarverzorging , soortmondverzorging , sterkte , type, typehaarkleuring , typetandenborstel , variant,waterproof , pc.category, sub_category, sub_sub_category, brand, gender
                                                        from product_properties pp
                                                        inner join product_categories pc on pc.product_id = pp.product_id
                                                        inner join products on products.product_id = pp.product_id """,
            "")

        # replacing ' with '' so LIKE in the sql statement doesn't crash
        id_list = [id[0].replace("'", "''") for id in data]

        for count, id in enumerate(id_list):
            data = (property_matching(id, 4, price_data))
            recs = data[0]
            weight = data[1]

            cursor.execute(
                f"insert into property_matching_recs (product_id,recommendations,weighted_match_rate) values(%s,%s,%s)",
                (id, recs, weight))
            if count % 500 == 0 or count == len(id_list):
                connection.commit()
                print(
                    f"Recommendations (Property matching): {count}/{len(id_list)}"
                )
Example #4
0
def property_matching(product_id, limit, price_data):
    """Look at all relevant product properties to decide the best matching recommendation, returns a list with a list of recs and an average match rate"""

    # column numbers have been divided in different weight classes, product properties have different weight in deciding what to recommend
    w2 = [3, 4, 7, 8, 9, 12, 13, 15, 16, 17, 18, 19, 20, 22, 24, 25, 27]
    w5 = [5, 11, 21, 23, 26, 28]

    try:
        product_id_properties = database.execute_query(
            f"""select pc.product_id , doelgroep , eenheid, factor, gebruik, geschiktvoor, geursoort, huidconditie,huidtype , huidtypegezicht , inhoud , klacht , kleur , leeftijd , serie, soort,soorthaarverzorging , soortmondverzorging , sterkte , type, typehaarkleuring , typetandenborstel , variant,waterproof , pc.category, sub_category, sub_sub_category, brand, gender
                                                               from product_properties pp
                                                               inner join product_categories pc on pc.product_id = pp.product_id
                                                               inner join products on products.product_id = pp.product_id
                                                               where pc.product_id like '{product_id}'""",
            "")[0]
    except IndexError:
        return [None, 0]

    if (product_id_properties.count(None) == 27):
        return [None, 0]

    match_list = []
    for data in price_data:
        match_count = 0
        viable_match_len = 0
        for i in range(len(data)):
            # make sure we don't count None <-> None as a property match
            if not (data[i] is None or product_id_properties[i] is None):
                if data[i] != "}":

                    viable_match_len += 1
                    if data[i] == product_id_properties[i]:

                        if i in w2:
                            match_count += 2
                        elif i in w5:
                            match_count += 5
                        else:
                            match_count += 1

        match_list.append([data[0], match_count])

    # sort on match_count
    match_list = (list(reversed(sorted(match_list, key=lambda x: x[1]))))

    # change match_counts to a %-weighted-match scale
    scale_factor = float(100 / match_list[0][1])
    for index, x in enumerate(match_list):
        match_list[index][1] = round(x[1] * scale_factor, 2)

    # get the product_ids, exclude the best match(itself)
    recommendations = [match_list[1:][x][0] for x in range(limit)]

    avg_match = (round(
        sum([x[1] for x in match_list[1:limit + 1]]) / limit, 2))
    return [recommendations, avg_match]
Example #5
0
def recommend(cart, limit=4):
    """
        Generate recommendations for items in a shopping cart, based on the similarities between the cart and other orders
    """

    # Get all of the products in all of the orders
    res = database.execute_query(
        "select products from orders where cardinality(products) > 1", ())
    products = [set(e[0]) for e in res]

    # Sort the list of orders by how similar they are to the cart, and by how much the cart makes up of that order,
    # while simultaneously changing all orders to contain only products not found in the shopping cart
    cart = set(cart)
    products = sorted([
        e - cart for e in sorted(products, key=lambda e: len(cart & e))[::-1]
        if len(e - cart) > 0 and len(e & cart) >= 1
    ],
                      key=len)
    result = []

    # Get all legitimate IDs, to check whether the IDs in the orders are legit
    ids = [
        e[0]
        for e in database.execute_query("select product_id from products", "")
    ]

    # Fill the result with items from the changed products list, until the result contains a maximum number of items
    # equal to the given limit
    while len(result) < limit:
        if len(products) == 0:
            break

        order = products.pop(0)

        for p in order:
            if p not in result and p in ids:
                result.append(p)
                if len(result) >= limit:
                    break

    return result
Example #6
0
 def changeprofileid(self):
     """ This function checks whether the provided session ID actually exists
     and stores it in the session if it does. """
     try:
         newprofileid = request.form.get('profile_id')
         available_profiles = database.execute_query("select visitor_id from visitor_recs where visitor_id = %s",
                                                     (newprofileid,))
         profidexists = available_profiles[0][0] == newprofileid
         if profidexists:
             session['profile_id'] = newprofileid
             return '{"success":true}'
         return '{"success":false}'
     except:
         return '{"success":false}'
Example #7
0
    def productpage(self, cat1=None, cat2=None, cat3=None, cat4=None, page=1):
        """ This function renders the product page template with the products it
        can retrieve from the database, based on the URL path provided (which
        corresponds to product categories). """
        limit = session['items_per_page'] if session['items_per_page'] != 0 else \
            database.execute_query("select count(product_id) from products", '')[0][0]

        rec_limit = 4
        catlist = [cat1, cat2, cat3, cat4]
        nononescats = [e for e in catlist if e is not None]
        skipindex = session['items_per_page'] * (page - 1)

        """ Get all products (this need to be based on profile if has profile id) """
        profile_id = session['profile_id'] if session['profile_id'] is not None else '5a393d68ed295900010384ca'
        retrieved_ids = page_home.get_recommendations(profile_id, nononescats, limit)

        id_batch = retrieved_ids[skipindex:(skipindex + limit)]

        """ Convert the recommended ids to product objects """
        prodList = [convert_to_model.toProduct(e) for e in id_batch]

        """ Get 'anderen kochten ook' recommendations """
        recs = page_home.get_anderen_kochten_ook(id_batch, rec_limit, profile_id)
        recs = [convert_to_model.toProduct(e) if type(e) != product.Product else e for e in recs]

        """ Set the url path to match the categries and page we are in """
        if len(nononescats) > 0:
            pagepath = "/producten/" + ("/".join(nononescats)) + "/"
        else:
            pagepath = "/producten/"

        return self.renderpackettemplate('products.html', {'products': prodList,
                                                           'productcount': len(retrieved_ids),
                                                           'pstart': skipindex + 1,
                                                           'pend': skipindex + session['items_per_page'] if session[
                                                                                                                'items_per_page'] > 0 else len(
                                                               retrieved_ids),
                                                           'prevpage': pagepath + str(page - 1) if (
                                                                   page > 1) else False,
                                                           'nextpage': pagepath + str(page + 1) if (session[
                                                                                                        'items_per_page'] * page < len(
                                                               retrieved_ids)) else False,
                                                           'r_products': recs[:rec_limit],
                                                           'r_type': list(self.recommendationtypes.keys())[0],
                                                           'r_string': list(self.recommendationtypes.values())[0],
                                                           'header_text': 'Voor u aanbevolen' if len(
                                                               nononescats) == 0 else ''
                                                           })
Example #8
0
def product_detail_alg_selection(product):
    "code that decides what algorithm to use in the product_details based on the accuracy of the recommendations"

    recs_data = database.execute_query(
        f"select recommendations, weighted_match_rate from property_matching_recs where product_id = '{product.product_id}'",
        "")

    threshold = 50
    # If 50% (threshold value in percentages) of the properties of a product match with others
    # we can you those products. This indicated how much a product has in common with others.
    if recs_data[0][1] > threshold:
        print('Algorithm: property_matching')
        recs = (recs_data[0][0])
        r_products = convert_to_model.convert_to_product_list(
            "select * from products where product_id in %s", (tuple(recs), ))
    # if this product does have much in common we call the simple algorithm to extend te list to its limit (usually 4)
    else:
        print('Algorithm: simple')
        r_products = simple.get_recommendations(product.product_id)

    return r_products
Example #9
0
def cart_alg_selection(limit, shopping_cart, profile_id):
    "code that decides what algorithm to use in the shopping cart based on the accuracy of the recommendations, returns *limit* recommendations"

    # get all the ids from the cart
    ids_in_cart = [x[0] for x in shopping_cart]

    # if there is only 1 element in the list Psycopg2 will crash. Hence the statement add a blank string.
    # (does not affect the results)
    if len(ids_in_cart) == 1:
        ids_in_cart.append('')
    if len(ids_in_cart) > 0:
        recs_data = database.execute_query(
            f"select * from order_based_recs where product_id in {tuple(ids_in_cart)}",
            "")
        recs_data = list(reversed(sorted(recs_data,
                                         key=lambda x: x[2])))[:limit]
        recs_data_simple = database.execute_query(
            f"select * from simplerecs where product_id in {tuple(ids_in_cart)}",
            "")
        recs_data_behaviour = behaviour.recommend(ids_in_cart, limit)
        # print(f'{ids_in_cart}, {recs_data_behaviour}')

        sample_size_limit = 10
        if recs_data[0][2] >= sample_size_limit:
            print('Algorithm: Bought_together')

            recs = list(
                set([
                    rec for data in recs_data if (data[2] > sample_size_limit)
                    for rec in data[1]
                ]))

            # recs = prioritze_discount.prioritize_discount(recs, limit)
            random.shuffle(recs)
            recs = recs[:4]
        else:
            if len(recs_data_behaviour) == limit:
                print('Algorithm: Behaviour')
                recs = recs_data_behaviour
            else:
                print('Algorithm: Simple')
                recs = list(
                    set([
                        z for x in recs_data_simple
                        for z in random.sample(x[1], k=len(x[1]))
                    ]))[:limit]

        print(recs)
        if len(recs) < 4:
            recs = list(
                set([
                    z for x in recs_data_simple
                    for z in random.sample(x[1], k=len(x[1]))
                ]))[:limit]
            print(recs)
        r_prods = convert_to_model.convert_to_product_list(
            "select * from products where product_id in %s", (tuple(recs), ))

    else:
        try:
            if profile_id is None:
                raise NameError
            r_prods = page_home.get_profile_recommendations(profile_id,
                                                            limit=limit)
        except NameError:
            r_prods = [
                convert_to_model.toProduct(e)
                for e in database.get_based_on_categories([], limit)
            ]

    return r_prods
Example #10
0
def convert_to_product_list(query, data):
    r_prods = [toProduct(e) for e in (database.execute_query(query, data))]
    return r_prods
Example #11
0
def get_recs(visitor_id, limit, only_ids=False):
    """ get recommendations based on visitor id """

    # get related products to a profile """
    profile_columns = list(
        database.execute_query(
            "select previously_recommended, viewed_before, similars from visitor_recs where visitor_id = %s",
            (visitor_id, ))[0])
    try:
        # flatten the lists (profile_columns can conatins list in lists etc.)
        all_product_ids = tuple(itertools.chain.from_iterable(profile_columns))
    except TypeError:
        return []

    # create dict with empty dicts for every category type and keep count
    category_counter = {'main': {}, 'sub': {}, 'sub_sub': {}}

    all_product_ids = list(all_product_ids)

    if (len(all_product_ids) == 1):
        all_product_ids.append('')

    if (len(all_product_ids) > 0):
        cat_results = database.execute_query(
            f"select category, sub_category, sub_sub_category from product_categories where product_id in {tuple(all_product_ids)}",
            "")
    else:
        cat_results = []

    layers = ['sub_sub', 'sub', 'main']
    products = []
    r_layers = layers.copy()
    r_layers.reverse()
    for row in cat_results:
        for i, layer in enumerate(r_layers):
            if row[i] in category_counter[layer]:
                category_counter[layer][row[i]] += 1
            else:
                category_counter[layer][row[i]] = 1

    # loop trough category types starting from deepest (starting wiht sub_sub)
    for layer in layers:
        try:
            # Calculate recommendations from the category type and add them to a list
            category_counter = {
                k: v
                for k, v in reversed(
                    sorted(category_counter[layer].items(),
                           key=lambda item: item[1]))
            }
            most_popular_category = list(category_counter.keys())[0]
            popular_category_product_ids = database.execute_query(
                "select product_id from product_categories where sub_sub_category = %s",
                (most_popular_category, ))
            popular_category_product_ids = prioritze_discount.prioritize_discount(
                list(
                    itertools.chain.from_iterable(
                        popular_category_product_ids)), limit)
            select_criteria = '*' if only_ids is False else 'product_id'
            products_results = database.execute_query(
                f"select {select_criteria} from products where product_id in %s",
                (tuple(popular_category_product_ids), ))
            return products_results
        except psycopg2.errors.SyntaxError:
            break
    return products