octorecommender 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|