octorecommender 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +0 -0
- data/lib/octorecommender.rb +20 -0
- data/lib/octorecommender/helpers.rb +2 -0
- data/lib/octorecommender/helpers/generic.rb +11 -0
- data/lib/octorecommender/helpers/record.rb +20 -0
- data/lib/octorecommender/octohooks.rb +31 -0
- data/lib/octorecommender/recommenders.rb +143 -0
- data/lib/octorecommender/recommenders/product_recommender.rb +33 -0
- data/lib/octorecommender/recommenders/time_recommender.rb +41 -0
- data/lib/octorecommender/scheduler.rb +19 -0
- data/lib/octorecommender/version.rb +7 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e8c71b55542fbe8ecfc53e19f2943491a5a1510d
|
4
|
+
data.tar.gz: 320d5056ee304ab7b94f82a71496d8b660c86d21
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3fa4e9b0d64fa9e6838d46924e89a92e8a5e6a79d19a2fdd3fb67c9a428084870836cca057fee2428c8525c75fb6794a42ce8c4cfb8b2018ca22d7ec400bac19
|
7
|
+
data.tar.gz: a437e579d27d2e103a9ff4f6a75bb14ba00b876174fecfae784753032ee2e382ec5f23fd5275dce1095ef36df73bc4d80bffb17b3da5fbd199d2f089c1d3d214
|
data/README.md
ADDED
File without changes
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'predictor'
|
2
|
+
require 'octocore'
|
3
|
+
|
4
|
+
require 'octorecommender/recommenders'
|
5
|
+
require 'octorecommender/version'
|
6
|
+
require 'octorecommender/helpers'
|
7
|
+
require 'octorecommender/octohooks'
|
8
|
+
require 'octorecommender/scheduler'
|
9
|
+
|
10
|
+
module Octo
|
11
|
+
|
12
|
+
# Override Octo's default connection to include
|
13
|
+
# connection to Predictor as well
|
14
|
+
# See Octo#connect
|
15
|
+
def self.connect(configuration)
|
16
|
+
self._connect(configuration)
|
17
|
+
Predictor.redis = Cequel::Record.redis
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Overriding Product class so as to add
|
2
|
+
# recommender related helper methods
|
3
|
+
module Octo
|
4
|
+
module Record
|
5
|
+
|
6
|
+
def similarities(opts={})
|
7
|
+
eid = self.enterprise.id
|
8
|
+
recommender.similar_products self, opts
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Generate the recommender instance
|
14
|
+
# @return [Octo::Recommender]
|
15
|
+
def recommender
|
16
|
+
@recommender = Octo::Recommender.new unless @recommender
|
17
|
+
@recommender
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'octocore/callbacks'
|
2
|
+
|
3
|
+
module Octo
|
4
|
+
|
5
|
+
module OctoHooks
|
6
|
+
|
7
|
+
# Define the custom tasks that need to be done
|
8
|
+
# upon various hooks
|
9
|
+
Octo::Callbacks.after_productpage_view lambda { |opts|
|
10
|
+
update_recommenders opts
|
11
|
+
}
|
12
|
+
|
13
|
+
# Extend the methods here
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
# Updates the recommenders
|
17
|
+
# @param [Hash] opts The options hash as passed
|
18
|
+
def update_recommenders(opts)
|
19
|
+
user = opts[:user]
|
20
|
+
product = opts[:product]
|
21
|
+
|
22
|
+
if user and product
|
23
|
+
recommender = Octo::Recommender.new
|
24
|
+
recommender.register_user_product_view(user, product)
|
25
|
+
recommender.register_user_action_time(user, Time.now.floor)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'predictor'
|
2
|
+
require 'octorecommender/recommenders/product_recommender'
|
3
|
+
require 'octorecommender/recommenders/time_recommender'
|
4
|
+
|
5
|
+
|
6
|
+
module Octo
|
7
|
+
class Recommender
|
8
|
+
|
9
|
+
# Queue for resque worker processing
|
10
|
+
@queue = :recommender
|
11
|
+
|
12
|
+
# Time format to convert a Time into HHMM format
|
13
|
+
TIME_HHMM = '%H%M'
|
14
|
+
|
15
|
+
# Initializes the Recommender.
|
16
|
+
def initialize
|
17
|
+
@product_recommenders = {}
|
18
|
+
@time_recommenders = {}
|
19
|
+
|
20
|
+
# This option chosen as ruby seems to take a LOOOOOOOOOOT of time
|
21
|
+
# in processing.
|
22
|
+
Octo::Recommenders::ProductRecommender.processing_technique(:union)
|
23
|
+
Octo::Recommenders::ProductRecommender.processing_technique :union
|
24
|
+
|
25
|
+
Octo::Enterprise.each do |enterprise|
|
26
|
+
@product_recommenders[enterprise.id] = Octo::Recommenders::ProductRecommender.new(enterprise.id)
|
27
|
+
@time_recommenders[enterprise.id] = Octo::Recommenders::TimeRecommender.new(enterprise.id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Register a user, product view relation.
|
32
|
+
# @param [Octo::User] user The user object
|
33
|
+
# @param [Octo::Product] product The product object
|
34
|
+
def register_user_product_view(user, product)
|
35
|
+
@product_recommenders[user.enterprise_id].add_to_matrix(:users,
|
36
|
+
user.id,
|
37
|
+
product.id
|
38
|
+
)
|
39
|
+
|
40
|
+
register_product product
|
41
|
+
end
|
42
|
+
|
43
|
+
# Register a Product for recommendation
|
44
|
+
# @param [Octo::Product] product The product object
|
45
|
+
def register_product(product)
|
46
|
+
eid = product.enterprise_id
|
47
|
+
product.categories.each do |cat_text|
|
48
|
+
@product_recommenders[eid].add_to_matrix(:categories,
|
49
|
+
cat_text,
|
50
|
+
product.id
|
51
|
+
)
|
52
|
+
end
|
53
|
+
product.tags.each do |tag_text|
|
54
|
+
@product_recommenders[eid].add_to_matrix(:tags,
|
55
|
+
tag_text,
|
56
|
+
product.id
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Register a user, action-time view relation.
|
62
|
+
# @param [Octo::User] user The user object
|
63
|
+
# @param [Time] ts The time at which user takes some action
|
64
|
+
def register_user_action_time(user, ts = Time.now.floor)
|
65
|
+
eid = user.enterprise_id
|
66
|
+
@time_recommenders[eid].add_to_matrix(:users,
|
67
|
+
user.id,
|
68
|
+
ts.to_i
|
69
|
+
)
|
70
|
+
|
71
|
+
@time_recommenders[eid].add_to_matrix(:hourminutes,
|
72
|
+
ts.strftime(TIME_HHMM),
|
73
|
+
ts.to_i
|
74
|
+
)
|
75
|
+
|
76
|
+
@time_recommenders[eid].add_to_matrix(:days,
|
77
|
+
ts.strftime('%A'),
|
78
|
+
ts.to_i
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Get recommended products for a user
|
83
|
+
# @param [Octo::User] user The user object for whom product
|
84
|
+
# recommendations to be fetched
|
85
|
+
# @return [Array<Octo::Product>] An array of Octo::Product recommended
|
86
|
+
def recommended_products(user)
|
87
|
+
eid = user.enterprise_id
|
88
|
+
recommendations = @product_recommenders[eid].predictions_for(
|
89
|
+
user.id,
|
90
|
+
matrix_label: :users
|
91
|
+
)
|
92
|
+
recommendations.collect do |x|
|
93
|
+
args = { enterprise_id: eid, id: x.to_i}
|
94
|
+
Octo::Product.get_cached(args)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get similar products for products
|
99
|
+
# @param [Octo::Product] product The product for which similarities
|
100
|
+
# have to be found
|
101
|
+
# @return [Array<Octo::Product>] An array containing similar
|
102
|
+
# products
|
103
|
+
def similar_products(product, opts={})
|
104
|
+
eid = product.enterprise.id
|
105
|
+
@product_recommenders[eid].similarities_for(product.id, opts)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Get recommended time for a user
|
109
|
+
# @param [Octo::User] user The user for whom time to be fetched
|
110
|
+
# @return [Array<Time>] The array of time recommended
|
111
|
+
def recommended_time(user)
|
112
|
+
eid = user.enterprise_id
|
113
|
+
recommendations = @time_recommenders[eid].predictions_for(user.id, matrix_label: :users)
|
114
|
+
recommendations.map do |ts|
|
115
|
+
convert_to_future(Time.at(ts.to_i))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Creates a delayed job to process all the recommenders for all the
|
120
|
+
# enterprises or can provide specific options as well
|
121
|
+
def process!(opts = {})
|
122
|
+
@product_recommenders.values.each do |recomm|
|
123
|
+
recomm.process!
|
124
|
+
end
|
125
|
+
@time_recommenders.values.each do |recomm|
|
126
|
+
recomm.process!
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Converts a time from past to a similar time in future.
|
131
|
+
# This is necessary as CF would return one of the dates
|
132
|
+
# from past. We need sometime from future.
|
133
|
+
def convert_to_future(ts)
|
134
|
+
ts + (Time.now.beginning_of_day - ts.beginning_of_day) + 7.day
|
135
|
+
end
|
136
|
+
|
137
|
+
# Callback method for resque worker
|
138
|
+
def self.perform
|
139
|
+
self.new.process!
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'predictor'
|
2
|
+
require 'octorecommender'
|
3
|
+
|
4
|
+
module Octo
|
5
|
+
module Recommenders
|
6
|
+
# The product recommender class. This class is responsible for recommending
|
7
|
+
# a (user, product)
|
8
|
+
class ProductRecommender
|
9
|
+
include Predictor::Base
|
10
|
+
|
11
|
+
def initialize(prefix)
|
12
|
+
@prefix = prefix
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_redis_prefix
|
16
|
+
@prefix
|
17
|
+
end
|
18
|
+
|
19
|
+
limit_similarities_to 20
|
20
|
+
|
21
|
+
# Stores the user and product relation
|
22
|
+
input_matrix :users, weight: 3.0
|
23
|
+
|
24
|
+
# Store the relation between products asserted by their tags
|
25
|
+
input_matrix :tags, weight: 2.0
|
26
|
+
|
27
|
+
# Store the relation between products asserted by their
|
28
|
+
# categories
|
29
|
+
input_matrix :categories, weight: 1.0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'predictor'
|
2
|
+
require 'octorecommender'
|
3
|
+
|
4
|
+
module Octo
|
5
|
+
|
6
|
+
module Recommenders
|
7
|
+
|
8
|
+
# The time recommender class. This class would be responsible for recommending
|
9
|
+
# the next set of time for a (user, action)
|
10
|
+
class TimeRecommender
|
11
|
+
include Predictor::Base
|
12
|
+
|
13
|
+
def initialize(prefix)
|
14
|
+
@prefix = prefix
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_redis_prefix
|
18
|
+
@prefix
|
19
|
+
end
|
20
|
+
|
21
|
+
limit_similarities_to 20
|
22
|
+
|
23
|
+
# This matrix stores the user and their action
|
24
|
+
input_matrix :users, weight: 1.0
|
25
|
+
|
26
|
+
# This matrix stores the relation between times in terms of
|
27
|
+
# HHMM.
|
28
|
+
# Ex:
|
29
|
+
# 11/3/2016 11:30 and 12/3/2016 11:30 have the same HHMM
|
30
|
+
input_matrix :hourminutes, weight: 3.0
|
31
|
+
|
32
|
+
# This matrix stores the relation between times in terms of
|
33
|
+
# days.
|
34
|
+
# Ex:
|
35
|
+
# 4/4/2016 (Monday) and 11/4/2016 (Monday) have the same day
|
36
|
+
input_matrix :days, weight: 2.0
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Octo
|
3
|
+
module Scheduler
|
4
|
+
|
5
|
+
# Setup Schedule for recommenders
|
6
|
+
def schedule_recommender
|
7
|
+
name = 'recommender_processing'
|
8
|
+
config = {
|
9
|
+
class: 'Octo::Recommender',
|
10
|
+
args: [],
|
11
|
+
cron: '0,30 * * * *',
|
12
|
+
persist: true,
|
13
|
+
queue: 'recommender'
|
14
|
+
}
|
15
|
+
Resque.set_schedule name, config
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: octorecommender
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pranav Prakash
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: predictor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.3.1
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.3.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.3.1
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.3.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: octocore
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 0.0.1
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.0.1
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 0.0.1
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.0.1
|
53
|
+
description: |2
|
54
|
+
Contains recommendation specific stuff
|
55
|
+
email: pp@octo.ai
|
56
|
+
executables: []
|
57
|
+
extensions: []
|
58
|
+
extra_rdoc_files:
|
59
|
+
- README.md
|
60
|
+
files:
|
61
|
+
- README.md
|
62
|
+
- lib/octorecommender.rb
|
63
|
+
- lib/octorecommender/helpers.rb
|
64
|
+
- lib/octorecommender/helpers/generic.rb
|
65
|
+
- lib/octorecommender/helpers/record.rb
|
66
|
+
- lib/octorecommender/octohooks.rb
|
67
|
+
- lib/octorecommender/recommenders.rb
|
68
|
+
- lib/octorecommender/recommenders/product_recommender.rb
|
69
|
+
- lib/octorecommender/recommenders/time_recommender.rb
|
70
|
+
- lib/octorecommender/scheduler.rb
|
71
|
+
- lib/octorecommender/version.rb
|
72
|
+
homepage: http://phab.octo.ai/diffusion/GEMS/
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '2.0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.4.6
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Octo Recommender(s) Module
|
96
|
+
test_files: []
|
97
|
+
has_rdoc: true
|