diff --git a/recommendation_engine.py b/recommendation_engine.py index b3d9e4f..55fbe53 100644 --- a/recommendation_engine.py +++ b/recommendation_engine.py @@ -43,8 +43,9 @@ class Engine: else: self.state = { 'channels': { - 'globalKeywords': {'locked': False, 'value': 8}, - 'userKeywords': {'locked': False, 'value': 8} + 'globalKeywords': {'locked': False, 'value': 7}, + 'userKeywords': {'locked': False, 'value': 7}, + 'screenings': {'locked': True, 'value': 2} }, 'globalKeywords': {}, } @@ -54,11 +55,6 @@ class Engine: 'nextPlaylist': {'locked': False, 'value': 4}, 'staySame': {'locked': False, 'value': 8} } - if 'userKeywordsWeights' not in self.state: - self.state['userKeywordsWeights'] = { - 'themeTags': {'locked': False, 'value': 0.3}, - 'characterTags': {'locked': False, 'value': 0.7} - } self.update_keywords() @property @@ -201,12 +197,12 @@ class Engine: } - +# NOTE for future improvement: vids_exclude element unit could be clip or in/out time pairs, rather than playlist. +# The same playlist could be played in the grid view as long as these are differenct clips or separate times. def get_recommendations(self, user, vids_exclude = []): channels = {k: v.get('value', 0) for k, v in self.state['channels'].items()} sliders = {k: v.get('value', 0) for k, v in self.state['globalKeywords'].items()} gridChange = {k: v.get('value', 0) for k, v in self.state['gridChange'].items()} - userKeywordsWeights = {k: v.get('value', 1) for k, v in self.state['userKeywordsWeights'].items()} # Exclude playlists from the most recent grid playlists = copy.deepcopy(self.playlists) @@ -215,26 +211,13 @@ class Engine: if playlist["name"] in vids_exclude: playlists.remove(playlist) - # For each playlist, compute user keyword score by theme and character tags - user_keywords = copy.deepcopy(user.get('keywords', {})) - theme_tags = {k.lower():v for k,v in user_keywords.items() if not k.isupper()} - character_tags = {k:v for k,v in user_keywords.items() if k.isupper()} - # manually modify some of the user keywords to match the playlist tags - theme_tags["god"] = theme_tags.get("god - gods",0) - theme_tags["visionary"] = theme_tags.get("visionary - enlightenment",0) - theme_tags["enlightenment"] = theme_tags.get("visionary - enlightenment",0) - character_tags["FEDOR MIKHAILOVICH SOFRONOV"] = character_tags.get("FYODOR MIKHAILOVICH SOFRONOV",0) - character_tags["SHKABARNYA OLGA SERGEEVNA"] = character_tags.get("OLGA SERGEEVNA SHKABARNYA",0) - character_tags["VICTORIA OLEGOVNA SKITSKAYA"] = character_tags.get("VIKTORIA OLEGOVNA SKITSKAYA",0) - + # For each playlist, compute user keyword score + user_keywords = user.get('keywords', {}) score = {} for playlist in playlists: - score[playlist['name']] = random.random() * 0.001 - for tag in playlist['tags']: - if tag in theme_tags: - score[playlist['name']] += theme_tags[tag] * userKeywordsWeights["themeTags"] - elif tag in character_tags: - score[playlist['name']] += character_tags[tag] * userKeywordsWeights["characterTags"] + score[playlist['name']] = random.random() + for tag in [tag for tag in playlist['tags'] if tag in user_keywords]: + score[playlist['name']] += user_keywords[tag] # Select highest scoring playlists playlists = sorted( playlists, @@ -253,7 +236,23 @@ class Engine: playlists, key=lambda playlist: -score[playlist['name']] ) - videos += playlists[:16 - channels['userKeywords']] + videos += playlists[:channels['globalKeywords']] + playlists = playlists[channels['globalKeywords']:] + # Count products the user has seen + count = defaultdict(lambda: 0) + for event in user.get('events', []): + if event.get('data', {}).get('product'): + count[event['data']['product']] += 1 + # For each product in playlist tags, increment score by count + for playlist in playlists: + score[playlist['name']] = random.random() + for tag in set(playlist['tags']) & set(count): + score[playlist['name']] += count[tag] + # Select highest scoring playlists + videos += sorted( + playlists, + key=lambda playlist: -score[playlist['name']] + )[:16 - channels['userKeywords'] - channels['globalKeywords']] # Shuffle playlists (randomize layout) and shift clips (randomize start) random.shuffle(videos) return [{