octocore 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +117 -0
- data/README.md +50 -0
- data/Rakefile +138 -0
- data/bin/fakestream +258 -0
- data/lib/octocore.rb +141 -0
- data/lib/octocore/baseline.rb +131 -0
- data/lib/octocore/callbacks.rb +78 -0
- data/lib/octocore/config.rb +37 -0
- data/lib/octocore/config/config.yml +1 -0
- data/lib/octocore/config/search/index/user.yml +42 -0
- data/lib/octocore/counter.rb +265 -0
- data/lib/octocore/counter/helpers.rb +168 -0
- data/lib/octocore/helpers.rb +6 -0
- data/lib/octocore/helpers/api_consumer_helper.rb +371 -0
- data/lib/octocore/helpers/api_helper.rb +53 -0
- data/lib/octocore/helpers/api_logger.rb +14 -0
- data/lib/octocore/helpers/client_helper.rb +104 -0
- data/lib/octocore/helpers/kong_helper.rb +156 -0
- data/lib/octocore/helpers/sinatra_helper.rb +22 -0
- data/lib/octocore/kafka_bridge.rb +60 -0
- data/lib/octocore/kldivergence.rb +14 -0
- data/lib/octocore/models.rb +260 -0
- data/lib/octocore/models/contactus.rb +17 -0
- data/lib/octocore/models/enterprise.rb +76 -0
- data/lib/octocore/models/enterprise/api_event.rb +14 -0
- data/lib/octocore/models/enterprise/api_hit.rb +20 -0
- data/lib/octocore/models/enterprise/api_track.rb +13 -0
- data/lib/octocore/models/enterprise/app_init.rb +13 -0
- data/lib/octocore/models/enterprise/app_login.rb +12 -0
- data/lib/octocore/models/enterprise/app_logout.rb +12 -0
- data/lib/octocore/models/enterprise/authorization.rb +61 -0
- data/lib/octocore/models/enterprise/category.rb +14 -0
- data/lib/octocore/models/enterprise/category_baseline.rb +19 -0
- data/lib/octocore/models/enterprise/category_hit.rb +26 -0
- data/lib/octocore/models/enterprise/category_trend.rb +19 -0
- data/lib/octocore/models/enterprise/conversions.rb +69 -0
- data/lib/octocore/models/enterprise/ctr.rb +54 -0
- data/lib/octocore/models/enterprise/dimension_choice.rb +21 -0
- data/lib/octocore/models/enterprise/engagement_time.rb +43 -0
- data/lib/octocore/models/enterprise/funnel_data.rb +20 -0
- data/lib/octocore/models/enterprise/funnels.rb +126 -0
- data/lib/octocore/models/enterprise/gcm.rb +21 -0
- data/lib/octocore/models/enterprise/newsfeed_hit.rb +52 -0
- data/lib/octocore/models/enterprise/notification_hit.rb +42 -0
- data/lib/octocore/models/enterprise/page.rb +15 -0
- data/lib/octocore/models/enterprise/page_view.rb +14 -0
- data/lib/octocore/models/enterprise/pageload_time.rb +43 -0
- data/lib/octocore/models/enterprise/product.rb +22 -0
- data/lib/octocore/models/enterprise/product_baseline.rb +20 -0
- data/lib/octocore/models/enterprise/product_hit.rb +26 -0
- data/lib/octocore/models/enterprise/product_page_view.rb +13 -0
- data/lib/octocore/models/enterprise/product_trend.rb +18 -0
- data/lib/octocore/models/enterprise/push_key.rb +15 -0
- data/lib/octocore/models/enterprise/rules.rb +45 -0
- data/lib/octocore/models/enterprise/segment.rb +65 -0
- data/lib/octocore/models/enterprise/segment_data.rb +22 -0
- data/lib/octocore/models/enterprise/tag.rb +14 -0
- data/lib/octocore/models/enterprise/tag_baseline.rb +19 -0
- data/lib/octocore/models/enterprise/tag_hit.rb +26 -0
- data/lib/octocore/models/enterprise/tag_trend.rb +19 -0
- data/lib/octocore/models/enterprise/template.rb +18 -0
- data/lib/octocore/models/plans.rb +17 -0
- data/lib/octocore/models/subscribe.rb +12 -0
- data/lib/octocore/models/user.rb +22 -0
- data/lib/octocore/models/user/push_token.rb +15 -0
- data/lib/octocore/models/user/user_browser_details.rb +16 -0
- data/lib/octocore/models/user/user_location_history.rb +15 -0
- data/lib/octocore/models/user/user_persona.rb +101 -0
- data/lib/octocore/models/user/user_phone_details.rb +17 -0
- data/lib/octocore/models/user/user_profile.rb +20 -0
- data/lib/octocore/models/user/user_timeline.rb +111 -0
- data/lib/octocore/record.rb +20 -0
- data/lib/octocore/schedeuleable.rb +20 -0
- data/lib/octocore/scheduler.rb +59 -0
- data/lib/octocore/search.rb +5 -0
- data/lib/octocore/search/client.rb +33 -0
- data/lib/octocore/search/indexer.rb +0 -0
- data/lib/octocore/search/searchable.rb +18 -0
- data/lib/octocore/search/setup.rb +71 -0
- data/lib/octocore/segment.rb +285 -0
- data/lib/octocore/stats.rb +33 -0
- data/lib/octocore/trendable.rb +88 -0
- data/lib/octocore/trends.rb +158 -0
- data/lib/octocore/utils.rb +90 -0
- data/lib/octocore/version.rb +4 -0
- data/spec/lib/stats_spec.rb +20 -0
- data/spec/spec_helper.rb +103 -0
- metadata +436 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'cequel'
|
2
|
+
|
3
|
+
module Octo
|
4
|
+
class UserPhoneDetails
|
5
|
+
include Cequel::Record
|
6
|
+
|
7
|
+
belongs_to :user, class_name: 'Octo::User'
|
8
|
+
|
9
|
+
key :deviceid, :text
|
10
|
+
column :manufacturer, :text
|
11
|
+
column :model, :text
|
12
|
+
column :os, :text
|
13
|
+
|
14
|
+
timestamps
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'cequel'
|
2
|
+
|
3
|
+
module Octo
|
4
|
+
class UserProfileDetails
|
5
|
+
include Cequel::Record
|
6
|
+
|
7
|
+
belongs_to :user, class_name: 'Octo::User'
|
8
|
+
|
9
|
+
key :email, :text
|
10
|
+
column :username, :text
|
11
|
+
column :dob, :text
|
12
|
+
column :gender, :text
|
13
|
+
column :alternate_email, :text
|
14
|
+
column :mobile, :text
|
15
|
+
column :extras, :text
|
16
|
+
|
17
|
+
timestamps
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'cequel'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Octo
|
5
|
+
class UserTimeline
|
6
|
+
include Cequel::Record
|
7
|
+
|
8
|
+
BROWSE_PRODUCT = 0
|
9
|
+
BROWSE_PAGE = 1
|
10
|
+
SEARCH = 2
|
11
|
+
SHARE = 3
|
12
|
+
ADD_TO_CART = 4
|
13
|
+
CHECKOUT = 5
|
14
|
+
APP_OPEN = 6
|
15
|
+
APP_CLOSE = 7
|
16
|
+
PAGE_RELOAD = 8
|
17
|
+
|
18
|
+
LOC_HOME = 11
|
19
|
+
LOC_OFFICE = 12
|
20
|
+
LOC_TRANSIT = 13
|
21
|
+
LOC_VACATION = 14
|
22
|
+
LOC_OOH = 15
|
23
|
+
LOC_OTHERS = 16
|
24
|
+
|
25
|
+
belongs_to :user, class_name: 'Octo::User'
|
26
|
+
|
27
|
+
key :ts, :timestamp
|
28
|
+
|
29
|
+
column :type, :int
|
30
|
+
column :title, :text
|
31
|
+
column :location_type, :int
|
32
|
+
column :insight, :text
|
33
|
+
column :details, :text
|
34
|
+
|
35
|
+
timestamps
|
36
|
+
|
37
|
+
def self.fakedata(user, n = rand(7..20))
|
38
|
+
Array.new(3*n) do |i|
|
39
|
+
i+1
|
40
|
+
end.shuffle.sample(n).sort.reverse.collect do |i|
|
41
|
+
args = {
|
42
|
+
user: user,
|
43
|
+
ts: i.minutes.ago,
|
44
|
+
type: rand(0..8),
|
45
|
+
title: 'Product Name',
|
46
|
+
location_type: rand(11..16),
|
47
|
+
insight: 'some valueable insight',
|
48
|
+
details: 'other details here'
|
49
|
+
}
|
50
|
+
self.new(args).save!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def location_text(location_type)
|
55
|
+
case location_type
|
56
|
+
when LOC_HOME
|
57
|
+
'Home'
|
58
|
+
when LOC_OFFICE
|
59
|
+
'Office'
|
60
|
+
when LOC_TRANSIT
|
61
|
+
'In Transit'
|
62
|
+
when LOC_VACATION
|
63
|
+
'While Vacation'
|
64
|
+
when LOC_OOH
|
65
|
+
'Out of Home City'
|
66
|
+
when LOC_OTHERS
|
67
|
+
'Other Location'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def type_text(activity_type)
|
72
|
+
case activity_type
|
73
|
+
when BROWSE_PRODUCT
|
74
|
+
'Browsed for Product'
|
75
|
+
when BROWSE_PAGE
|
76
|
+
'Browsed for Page'
|
77
|
+
when SEARCH
|
78
|
+
'Searched'
|
79
|
+
when SHARE
|
80
|
+
'Shared'
|
81
|
+
when ADD_TO_CART
|
82
|
+
'Added to Cart'
|
83
|
+
when CHECKOUT
|
84
|
+
'Performed Checkout'
|
85
|
+
when APP_OPEN
|
86
|
+
'Opened App'
|
87
|
+
when APP_CLOSE
|
88
|
+
'Closed App'
|
89
|
+
when PAGE_RELOAD
|
90
|
+
'Reloaded Page'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def human_readable
|
95
|
+
args = {
|
96
|
+
user: self.user,
|
97
|
+
ts: self.ts,
|
98
|
+
type: type_text(self.type),
|
99
|
+
type_raw: self.type,
|
100
|
+
title: self.title,
|
101
|
+
location: location_text(self.location_type),
|
102
|
+
location_raw: self.location_type,
|
103
|
+
insight: self.insight,
|
104
|
+
details: self.details
|
105
|
+
}
|
106
|
+
OpenStruct.new(args)
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Octo
|
2
|
+
module Record
|
3
|
+
|
4
|
+
def unique_id
|
5
|
+
candidates = self.key_attributes
|
6
|
+
if candidates.length == 1
|
7
|
+
# This is most likely going to be the enterpriseid of some sort
|
8
|
+
candidates.first[1].to_s
|
9
|
+
elsif candidates.length == 2
|
10
|
+
if candidates.has_key?(:enterprise_id)
|
11
|
+
candidates.delete(:enterprise_id)
|
12
|
+
candidates.first[1].to_s
|
13
|
+
end
|
14
|
+
else
|
15
|
+
raise NotImplementedError, 'See Octo::Record#unique_id'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'octocore/counter'
|
2
|
+
|
3
|
+
module Octo
|
4
|
+
module Scheduleable
|
5
|
+
|
6
|
+
def perform(*args)
|
7
|
+
type = args[0].to_sym
|
8
|
+
|
9
|
+
if Octo::Counter.constants.include?type
|
10
|
+
if type == :TYPE_MINUTE and self.respond_to?(:aggregate!)
|
11
|
+
aggregate!
|
12
|
+
else
|
13
|
+
method_name = type_counters_method_names type
|
14
|
+
send(method_name.to_sym, Time.now.floor)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'resque'
|
3
|
+
require 'resque/tasks'
|
4
|
+
require 'resque/scheduler/tasks'
|
5
|
+
|
6
|
+
# Make sure dynamic scheduling is turned ON
|
7
|
+
Resque::Scheduler.dynamic = true
|
8
|
+
|
9
|
+
module Octo
|
10
|
+
module Scheduler
|
11
|
+
|
12
|
+
# Setup the schedules for counters.
|
13
|
+
def schedule_counters
|
14
|
+
counter_classes = [
|
15
|
+
Octo::ProductHit,
|
16
|
+
Octo::CategoryHit,
|
17
|
+
Octo::TagHit,
|
18
|
+
Octo::ApiHit,
|
19
|
+
Octo::NewsfeedHit
|
20
|
+
]
|
21
|
+
counter_classes.each do |clazz|
|
22
|
+
clazz.send(:get_typecounters).each do |counter|
|
23
|
+
name = [clazz, counter].join('::')
|
24
|
+
config = {
|
25
|
+
class: clazz.to_s,
|
26
|
+
args: [counter],
|
27
|
+
cron: '* * * * *',
|
28
|
+
persist: true,
|
29
|
+
queue: 'high'
|
30
|
+
}
|
31
|
+
Resque.set_schedule name, config
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Schedules the processing of baselines
|
36
|
+
def schedule_baseline
|
37
|
+
baseline_classes = [
|
38
|
+
Octo::ProductBaseline,
|
39
|
+
Octo::CategoryBaseline,
|
40
|
+
Octo::TagBaseline
|
41
|
+
]
|
42
|
+
baseline_classes.each do |clazz|
|
43
|
+
clazz.send(:get_typecounters).each do |counter|
|
44
|
+
name = [clazz, counter].join('::')
|
45
|
+
config = {
|
46
|
+
class: clazz.to_s,
|
47
|
+
args: [counter],
|
48
|
+
cron: '* * * * *',
|
49
|
+
persists: true,
|
50
|
+
queue: 'baseline_processing'
|
51
|
+
}
|
52
|
+
Resque.set_schedule name, config
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'faraday'
|
3
|
+
require 'elasticsearch/api'
|
4
|
+
|
5
|
+
module Octo
|
6
|
+
|
7
|
+
# Search wrapper around ElasticSearch
|
8
|
+
module Search
|
9
|
+
|
10
|
+
class Client
|
11
|
+
|
12
|
+
include Elasticsearch::API
|
13
|
+
|
14
|
+
CONNECTION = ::Faraday::Connection.new(url: Octo.get_config(:search)[:server])
|
15
|
+
|
16
|
+
# Low level method for performing a request to Elastic Search cluster
|
17
|
+
# @param [String] method The method ex: get, put, post, etc..
|
18
|
+
# @param [String] path The path of the request
|
19
|
+
# @param [Hash] params The params of the request
|
20
|
+
# @param [String] body The body of the request
|
21
|
+
def perform_request(method, path, params, body)
|
22
|
+
Octo.logger.debug "--> #{method.upcase} #{path} #{params} #{body}"
|
23
|
+
|
24
|
+
CONNECTION.run_request \
|
25
|
+
method.downcase.to_sym,
|
26
|
+
path,
|
27
|
+
( body ? MultiJson.dump(body): nil ),
|
28
|
+
{'Content-Type' => 'application/json'}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Octo
|
2
|
+
module Searchable
|
3
|
+
|
4
|
+
# Gets the search client
|
5
|
+
def searchclient
|
6
|
+
unless @searchClient
|
7
|
+
@searchClient = Octo::Search::Client.new
|
8
|
+
end
|
9
|
+
@searchClient
|
10
|
+
end
|
11
|
+
|
12
|
+
# Defines the indice with which this would be indexed
|
13
|
+
def indexable_with(indice_name, type)
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Octo
|
2
|
+
|
3
|
+
# Setup module for ElasticSearch
|
4
|
+
module Setup
|
5
|
+
|
6
|
+
# Creates the necessary indices
|
7
|
+
class Create
|
8
|
+
|
9
|
+
def self.perform
|
10
|
+
sclient = Octo::Search::Client.new
|
11
|
+
sconfig = Octo.get_config(:search)
|
12
|
+
|
13
|
+
# Set the cluster disk space thresholds first. That's necessary
|
14
|
+
# because the defaults are too less for development machines. So,
|
15
|
+
# in order to keep it moving, we set a lower threshold in development.
|
16
|
+
# Refer
|
17
|
+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/disk-allocator.html
|
18
|
+
if sconfig.has_key?(:disk_threshold_low) and sconfig.has_key?(:disk_threshold_high)
|
19
|
+
cluster_settings = {
|
20
|
+
body: {
|
21
|
+
persistent: {
|
22
|
+
'cluster.routing.allocation.disk.threshold_enabled' => true,
|
23
|
+
'cluster.routing.allocation.disk.watermark.low' => sconfig[:disk_threshold_low],
|
24
|
+
'cluster.routing.allocation.disk.watermark.high' => sconfig[:disk_threshold_high],
|
25
|
+
'cluster.info.update.interval' => '60s'
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
sclient.cluster.put_settings cluster_settings
|
30
|
+
end
|
31
|
+
|
32
|
+
# Check if any indices specified exists. If not exists, create them
|
33
|
+
sconfig[:index].keys.each do |index_name|
|
34
|
+
args = { index: index_name }
|
35
|
+
if sclient.indices.exists?(args)
|
36
|
+
Octo.logger.info "Search Index: #{ index_name } exists."
|
37
|
+
else
|
38
|
+
Octo.logger.warn "Search Index: #{ index_name } DOES NOT EXIST."
|
39
|
+
Octo.logger.info "Creating Index: #{ index_name }"
|
40
|
+
create_args = {
|
41
|
+
index: index_name,
|
42
|
+
body: sconfig[:index][index_name]
|
43
|
+
}
|
44
|
+
sclient.indices.create create_args
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Also check if there are any indices present that should not be
|
49
|
+
# present
|
50
|
+
_indices = JSON.parse(sclient.cluster.state)['metadata']['indices'].
|
51
|
+
keys.map(&:to_sym)
|
52
|
+
extra_indices = _indices - sconfig[:index].keys
|
53
|
+
Octo.logger.warn "Found extra indices: #{ extra_indices }"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Updates the indices.
|
58
|
+
# The major differene between this and the Create is that while create
|
59
|
+
# just checks for the existance by name, and passes if the name is found
|
60
|
+
# This actually overwrites all the mappings, properties, warmers etc
|
61
|
+
# So, this should be used only when we need to explicitly "UPDATE" the
|
62
|
+
# index.
|
63
|
+
class Update
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,285 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'octocore/helpers/api_consumer_helper'
|
4
|
+
|
5
|
+
module Octo
|
6
|
+
|
7
|
+
# The Segmentation module
|
8
|
+
module Segmentation
|
9
|
+
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
module Helpers
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Helper method for returning operators as a hash to be used in UX
|
17
|
+
# @param [Fixnum] dimension The dimension for which the operators
|
18
|
+
# are to be fetched
|
19
|
+
# @return [Array<Hash{Symbol => String }>] The hash containing key :text
|
20
|
+
# as the text to display, and another key :id as the id to be used
|
21
|
+
# as reference while communicating
|
22
|
+
def operators_as_choice(dimension = nil)
|
23
|
+
mapping_as_choice operator_text
|
24
|
+
end
|
25
|
+
|
26
|
+
# Helper method for returning dimensions as choice to be used in the UX
|
27
|
+
# @return [Array<Hash{Symbol => String }>] The hash containing key :text
|
28
|
+
# as the text to display, and another key :id as the id to be used
|
29
|
+
# as reference while communicating
|
30
|
+
def dimensions_as_choice
|
31
|
+
mapping_as_choice dimension_text
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns logic operatos as a choice for operating between dimensions
|
35
|
+
def logic_operators_as_choice
|
36
|
+
mapping_as_choice logic_text
|
37
|
+
end
|
38
|
+
|
39
|
+
# Helper method to return valid choices for a given dimension. It tries
|
40
|
+
# to find the values from db first. In case, there is nothing, it
|
41
|
+
# shows some default values, so the dashboard does not look totally
|
42
|
+
# blank.
|
43
|
+
# @param [Fixnum] dimension The dimension ID for which choices to be
|
44
|
+
# found
|
45
|
+
# @param [String] enterprise_id The enterprise ID for which the choices
|
46
|
+
# to be found
|
47
|
+
def choices_for_dimensions(dimension, enterprise_id)
|
48
|
+
args = {
|
49
|
+
enterprise_id: enterprise_id,
|
50
|
+
dimension: dimension
|
51
|
+
}
|
52
|
+
res = Octo::DimensionChoice.where(args)
|
53
|
+
choices = Array.new
|
54
|
+
if res.count > 0
|
55
|
+
choices = res.collect do |r|
|
56
|
+
r.column
|
57
|
+
end
|
58
|
+
elsif dimension_choice.has_key?(dimension)
|
59
|
+
func = dimension_choice[dimension]
|
60
|
+
choices = self.send(func, enterprise_id)
|
61
|
+
end
|
62
|
+
mapping_as_choice Hash[Array.new(choices.count) { |i| i }.zip(choices)]
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# Returns a hash containing keys as Operators and the values as string
|
69
|
+
# text corresponding to them.
|
70
|
+
def operator_text
|
71
|
+
{
|
72
|
+
Octo::Segmentation::Operators::EQUAL => '= Equals',
|
73
|
+
Octo::Segmentation::Operators::NOT_EQUAL => '!= Not Equals',
|
74
|
+
Octo::Segmentation::Operators::IN => 'Within range'
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return a hash containing keys as dimensions and the values as methods
|
79
|
+
# which should be called to populate the values
|
80
|
+
def dimension_choice
|
81
|
+
{
|
82
|
+
Octo::Segmentation::Dimensions::CITY => :city_choices,
|
83
|
+
Octo::Segmentation::Dimensions::STATE => :state_choices,
|
84
|
+
Octo::Segmentation::Dimensions::COUNTRY => :country_choices,
|
85
|
+
Octo::Segmentation::Dimensions::OS => :os_choices,
|
86
|
+
Octo::Segmentation::Dimensions::MANUFACTURER => :manufacturer_choices,
|
87
|
+
Octo::Segmentation::Dimensions::BROWSER => :browser_choices,
|
88
|
+
Octo::Segmentation::Dimensions::MODEL => :model_choices,
|
89
|
+
Octo::Segmentation::Dimensions::ENGAGEMENT => :engagement_choices
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns a hash containing the dimension value and the string text
|
94
|
+
def dimension_text
|
95
|
+
{
|
96
|
+
Octo::Segmentation::Dimensions::CITY => 'City',
|
97
|
+
Octo::Segmentation::Dimensions::STATE => 'State',
|
98
|
+
Octo::Segmentation::Dimensions::COUNTRY => 'Country',
|
99
|
+
Octo::Segmentation::Dimensions::OS => 'OS',
|
100
|
+
Octo::Segmentation::Dimensions::MANUFACTURER => 'Manufacturer',
|
101
|
+
Octo::Segmentation::Dimensions::BROWSER => 'Browser',
|
102
|
+
Octo::Segmentation::Dimensions::MODEL => 'Model',
|
103
|
+
Octo::Segmentation::Dimensions::ENGAGEMENT => 'Engagement',
|
104
|
+
Octo::Segmentation::Dimensions::LAST_ACTIVE => 'Last Active On',
|
105
|
+
Octo::Segmentation::Dimensions::CREATED_ON => 'Created On'
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
# Defines which operators can be used for which dimensions
|
110
|
+
def dimension_operator
|
111
|
+
ops = Set.new([Octo::Segmentation::Operators::EQUAL,
|
112
|
+
Octo::Segmentation::Operators::NOT_EQUAL,
|
113
|
+
Octo::Segmentation::Operators::IN])
|
114
|
+
Hash.new { |h,k| h[k] = ops }
|
115
|
+
end
|
116
|
+
|
117
|
+
def logic_text
|
118
|
+
{
|
119
|
+
Octo::Segmentation::Operators::AND => 'AND',
|
120
|
+
Octo::Segmentation::Operators::OR => 'OR',
|
121
|
+
Octo::Segmentation::Operators::NOT => 'NOT',
|
122
|
+
Octo::Segmentation::Operators::XOR => 'XOR',
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
# Generates the city choices for the enterprise
|
127
|
+
# @param [String] enterprise_id The enterpriseID for which city choices
|
128
|
+
# to be found
|
129
|
+
# @return [Array<String>] Array of string values
|
130
|
+
def city_choices(enterprise_id=nil)
|
131
|
+
['New Delhi', 'Mumbai', 'Bengaluru', 'San Francisco', 'Seattle']
|
132
|
+
end
|
133
|
+
|
134
|
+
def state_choices(enterprise_id=nil)
|
135
|
+
['Delhi', 'Maharashtra', 'Karnataka', 'California']
|
136
|
+
end
|
137
|
+
|
138
|
+
def country_choices(enterprise_id=nil)
|
139
|
+
['India', 'United States of America (USA)']
|
140
|
+
end
|
141
|
+
|
142
|
+
def os_choices(enterprise_id=nil)
|
143
|
+
['Windows', 'OS X', 'iOS', 'android']
|
144
|
+
end
|
145
|
+
|
146
|
+
def manufacturer_choices(enterprise_id=nil)
|
147
|
+
['Apple', 'Dell', 'HP', 'Samsung', 'Micromax']
|
148
|
+
end
|
149
|
+
|
150
|
+
def browser_choices(enterprise_id=nil)
|
151
|
+
['Firefox', 'Chrome', 'Safari']
|
152
|
+
end
|
153
|
+
|
154
|
+
def model_choices(enterprise_id=nil)
|
155
|
+
['iPhone 6', 'iPhone 6s', 'iPhone 5', 'Samsung S6']
|
156
|
+
end
|
157
|
+
|
158
|
+
def engagement_choices(enterprise_id=nil)
|
159
|
+
[Octo::UserPersona::HIGH_ENGAGED,
|
160
|
+
Octo::UserPersona::MEDIUM_ENGAGED,
|
161
|
+
Octo::UserPersona::LOW_ENGAGED,
|
162
|
+
Octo::UserPersona::DEAD].collect { |x| Octo::UserPersona.engaged_text(x) }
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
# Converts a hash mapping into choices ready for UX
|
167
|
+
# @return [Array<Hash{Symbol => String }>] The hash containing key :text
|
168
|
+
# as the text to display, and another key :id as the id to be used
|
169
|
+
# as reference while communicating
|
170
|
+
def mapping_as_choice(map)
|
171
|
+
map.inject([]) do | choices, pair |
|
172
|
+
key, val = pair
|
173
|
+
choices << { text: val, id: key }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
module SegmentType
|
181
|
+
|
182
|
+
USER = 0
|
183
|
+
EVENT = 1
|
184
|
+
end
|
185
|
+
|
186
|
+
# The Operators modules. Defines Operators and necessary methods around
|
187
|
+
module Operators
|
188
|
+
|
189
|
+
EQUAL = 0
|
190
|
+
NOT_EQUAL = 1
|
191
|
+
GTE = 2
|
192
|
+
GT = 3
|
193
|
+
LTE = 4
|
194
|
+
LT = 5
|
195
|
+
IN = 6
|
196
|
+
AND = 7
|
197
|
+
OR = 8
|
198
|
+
NOT = 9
|
199
|
+
XOR = 10
|
200
|
+
|
201
|
+
class << self
|
202
|
+
|
203
|
+
# Returns if the given operator is valid or not
|
204
|
+
def valid?(operator)
|
205
|
+
Set.new([EQUAL, NOT_EQUAL, GTE, GT, LTE, LT, IN]).include?(operator.to_i)
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# The Dimensions module. Defines the dimensions possible and its abstraction
|
212
|
+
module Dimensions
|
213
|
+
|
214
|
+
# Geographical Dimensions seems most obvious
|
215
|
+
CITY = 0
|
216
|
+
STATE = 1
|
217
|
+
COUNTRY = 2
|
218
|
+
|
219
|
+
# Followed by User's device details
|
220
|
+
OS = 3
|
221
|
+
MANUFACTURER = 4
|
222
|
+
BROWSER = 5
|
223
|
+
MODEL = 6
|
224
|
+
|
225
|
+
# Followed by User's engagement patterns
|
226
|
+
ENGAGEMENT = 7
|
227
|
+
|
228
|
+
# What about their last active, created dates
|
229
|
+
LAST_ACTIVE = 8
|
230
|
+
CREATED_ON = 9
|
231
|
+
|
232
|
+
# Usage Pattern
|
233
|
+
DAYTIME_USAGE = 10
|
234
|
+
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
# Extend
|
239
|
+
module ClassMethods
|
240
|
+
|
241
|
+
# Returns a boolean specifying if the segment is a valid choice or not
|
242
|
+
# @param [String] segment The string to be evaluated
|
243
|
+
# @return [Boolean] If the provided string exists in valid choices
|
244
|
+
def is_valid_segment segment
|
245
|
+
all_segment_choices.include?segment
|
246
|
+
end
|
247
|
+
|
248
|
+
# Returns all possible segment choices. Segment choices are the first
|
249
|
+
# data point that is picked on top of which segment would be made. It
|
250
|
+
# could be one of the supported octo events (eg: app.init etc) or users
|
251
|
+
def all_segment_choices
|
252
|
+
valid_events << :users
|
253
|
+
end
|
254
|
+
|
255
|
+
private
|
256
|
+
|
257
|
+
# Get all the valid events
|
258
|
+
# @return [Set<Symbol>] Valid events globally
|
259
|
+
def valid_events
|
260
|
+
Set.new(Octo.get_config(:allowed_events))
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
|
265
|
+
# Merges choices and returns the set with opts taken care of
|
266
|
+
# @param [Array] c The initial choices
|
267
|
+
# @param [Hash] opts Optional hash specifying anything to be exluded or
|
268
|
+
# included
|
269
|
+
# @option opts [Array<String>] :include Strings to be included in choices
|
270
|
+
# @option opts [Array<String>] :exclude Strings to be excluded in choices
|
271
|
+
# @return [Array<String>] Merged choices
|
272
|
+
def merge_choices(c, opts={})
|
273
|
+
Set.new(c).merge(
|
274
|
+
Set.new(opts.fetch(:include, []))
|
275
|
+
).subtract(
|
276
|
+
Set.new(opts.fetch(:exclude, []))
|
277
|
+
)
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|