octocore-cassandra 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +12 -0
  3. data/CONTRIBUTING +0 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +202 -0
  6. data/MAINTAINERS +0 -0
  7. data/NOTICE +8 -0
  8. data/README.md +69 -0
  9. data/Rakefile +142 -0
  10. data/bin/fakestream-cassandra +258 -0
  11. data/bin/octocore-admin-cassandra +54 -0
  12. data/lib/octocore-cassandra.rb +152 -0
  13. data/lib/octocore-cassandra/baseline.rb +131 -0
  14. data/lib/octocore-cassandra/callbacks.rb +117 -0
  15. data/lib/octocore-cassandra/config.rb +39 -0
  16. data/lib/octocore-cassandra/config/config.yml +1 -0
  17. data/lib/octocore-cassandra/config/search/index/user.yml +42 -0
  18. data/lib/octocore-cassandra/counter.rb +265 -0
  19. data/lib/octocore-cassandra/counter/helpers.rb +168 -0
  20. data/lib/octocore-cassandra/email.rb +63 -0
  21. data/lib/octocore-cassandra/featureflag.rb +79 -0
  22. data/lib/octocore-cassandra/helpers.rb +6 -0
  23. data/lib/octocore-cassandra/helpers/api_consumer_helper.rb +51 -0
  24. data/lib/octocore-cassandra/helpers/api_helper.rb +65 -0
  25. data/lib/octocore-cassandra/helpers/api_logger.rb +14 -0
  26. data/lib/octocore-cassandra/helpers/client_helper.rb +104 -0
  27. data/lib/octocore-cassandra/helpers/kong_helper.rb +164 -0
  28. data/lib/octocore-cassandra/helpers/sinatra_helper.rb +22 -0
  29. data/lib/octocore-cassandra/kafka_bridge.rb +60 -0
  30. data/lib/octocore-cassandra/kldivergence.rb +14 -0
  31. data/lib/octocore-cassandra/mailer.rb +1 -0
  32. data/lib/octocore-cassandra/mailer/subscriber_mailer.rb +30 -0
  33. data/lib/octocore-cassandra/message_parser.rb +114 -0
  34. data/lib/octocore-cassandra/models.rb +274 -0
  35. data/lib/octocore-cassandra/models/contactus.rb +42 -0
  36. data/lib/octocore-cassandra/models/enterprise.rb +76 -0
  37. data/lib/octocore-cassandra/models/enterprise/adapter_details.rb +18 -0
  38. data/lib/octocore-cassandra/models/enterprise/api_event.rb +14 -0
  39. data/lib/octocore-cassandra/models/enterprise/api_hit.rb +20 -0
  40. data/lib/octocore-cassandra/models/enterprise/api_key.rb +11 -0
  41. data/lib/octocore-cassandra/models/enterprise/api_track.rb +13 -0
  42. data/lib/octocore-cassandra/models/enterprise/app_init.rb +13 -0
  43. data/lib/octocore-cassandra/models/enterprise/app_login.rb +12 -0
  44. data/lib/octocore-cassandra/models/enterprise/app_logout.rb +12 -0
  45. data/lib/octocore-cassandra/models/enterprise/authorization.rb +67 -0
  46. data/lib/octocore-cassandra/models/enterprise/category.rb +14 -0
  47. data/lib/octocore-cassandra/models/enterprise/category_baseline.rb +19 -0
  48. data/lib/octocore-cassandra/models/enterprise/category_hit.rb +26 -0
  49. data/lib/octocore-cassandra/models/enterprise/category_trend.rb +19 -0
  50. data/lib/octocore-cassandra/models/enterprise/conversions.rb +69 -0
  51. data/lib/octocore-cassandra/models/enterprise/ctr.rb +54 -0
  52. data/lib/octocore-cassandra/models/enterprise/dimension_choice.rb +21 -0
  53. data/lib/octocore-cassandra/models/enterprise/engagement_time.rb +43 -0
  54. data/lib/octocore-cassandra/models/enterprise/funnel_data.rb +20 -0
  55. data/lib/octocore-cassandra/models/enterprise/funnel_tracker.rb +19 -0
  56. data/lib/octocore-cassandra/models/enterprise/funnels.rb +129 -0
  57. data/lib/octocore-cassandra/models/enterprise/gcm.rb +21 -0
  58. data/lib/octocore-cassandra/models/enterprise/newsfeed_hit.rb +52 -0
  59. data/lib/octocore-cassandra/models/enterprise/notification_hit.rb +42 -0
  60. data/lib/octocore-cassandra/models/enterprise/page.rb +15 -0
  61. data/lib/octocore-cassandra/models/enterprise/page_view.rb +14 -0
  62. data/lib/octocore-cassandra/models/enterprise/pageload_time.rb +43 -0
  63. data/lib/octocore-cassandra/models/enterprise/product.rb +22 -0
  64. data/lib/octocore-cassandra/models/enterprise/product_baseline.rb +20 -0
  65. data/lib/octocore-cassandra/models/enterprise/product_hit.rb +26 -0
  66. data/lib/octocore-cassandra/models/enterprise/product_page_view.rb +13 -0
  67. data/lib/octocore-cassandra/models/enterprise/product_trend.rb +18 -0
  68. data/lib/octocore-cassandra/models/enterprise/push_key.rb +15 -0
  69. data/lib/octocore-cassandra/models/enterprise/rules.rb +45 -0
  70. data/lib/octocore-cassandra/models/enterprise/segment.rb +65 -0
  71. data/lib/octocore-cassandra/models/enterprise/segment_data.rb +22 -0
  72. data/lib/octocore-cassandra/models/enterprise/tag.rb +14 -0
  73. data/lib/octocore-cassandra/models/enterprise/tag_baseline.rb +19 -0
  74. data/lib/octocore-cassandra/models/enterprise/tag_hit.rb +26 -0
  75. data/lib/octocore-cassandra/models/enterprise/tag_trend.rb +19 -0
  76. data/lib/octocore-cassandra/models/enterprise/template.rb +18 -0
  77. data/lib/octocore-cassandra/models/plans.rb +17 -0
  78. data/lib/octocore-cassandra/models/subscribe.rb +13 -0
  79. data/lib/octocore-cassandra/models/user.rb +15 -0
  80. data/lib/octocore-cassandra/models/user/push_token.rb +15 -0
  81. data/lib/octocore-cassandra/models/user/user_browser_details.rb +16 -0
  82. data/lib/octocore-cassandra/models/user/user_location_history.rb +15 -0
  83. data/lib/octocore-cassandra/models/user/user_persona.rb +101 -0
  84. data/lib/octocore-cassandra/models/user/user_phone_details.rb +17 -0
  85. data/lib/octocore-cassandra/models/user/user_profile.rb +20 -0
  86. data/lib/octocore-cassandra/models/user/user_timeline.rb +111 -0
  87. data/lib/octocore-cassandra/record.rb +20 -0
  88. data/lib/octocore-cassandra/schedeuleable.rb +20 -0
  89. data/lib/octocore-cassandra/scheduler.rb +72 -0
  90. data/lib/octocore-cassandra/search.rb +5 -0
  91. data/lib/octocore-cassandra/search/client.rb +33 -0
  92. data/lib/octocore-cassandra/search/indexer.rb +0 -0
  93. data/lib/octocore-cassandra/search/searchable.rb +18 -0
  94. data/lib/octocore-cassandra/search/setup.rb +71 -0
  95. data/lib/octocore-cassandra/segment.rb +287 -0
  96. data/lib/octocore-cassandra/stats.rb +33 -0
  97. data/lib/octocore-cassandra/trendable.rb +88 -0
  98. data/lib/octocore-cassandra/trends.rb +158 -0
  99. data/lib/octocore-cassandra/utils.rb +90 -0
  100. data/lib/octocore-cassandra/version.rb +4 -0
  101. data/spec/lib/stats_spec.rb +20 -0
  102. data/spec/spec_helper.rb +103 -0
  103. metadata +490 -0
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # A simple utility to generate fake stream of data
4
+ #
5
+ # Usage:
6
+ # ./fakestream
7
+
8
+ require 'net/http'
9
+ require 'json'
10
+ require 'octocore'
11
+
12
+ module Octo
13
+ class FakeStream
14
+
15
+ MAX_USERS = 100
16
+
17
+ MAX_PAGES = 30
18
+ MAX_PRODUCTS = 200
19
+
20
+ MAX_CATEGORIES = 10
21
+
22
+ MAX_TAGS = 15
23
+
24
+
25
+ #How many no. of pages to view before going to product page
26
+ MAX_PAGES_TO_VISIT = 3
27
+
28
+ # Initialize with the URL
29
+ def initialize
30
+ @_apikeys = {}
31
+ end
32
+
33
+ def base_url
34
+ Octo.get_config(:base_url)
35
+ end
36
+
37
+ def stream
38
+ user = fakeuser
39
+
40
+ # simulate an app login
41
+ app_init user
42
+ # simulate a random number of page views before going to product
43
+ for i in 1..(1+rand(MAX_PAGES_TO_VISIT))
44
+ pv user, fakepage(user.enterprise)
45
+ end
46
+
47
+ # simulate a product page view
48
+ ppv user, fakeproduct(user.enterprise)
49
+
50
+
51
+ #visit a page or not after going to the product page
52
+ for i in 0..rand(2)
53
+ pv user, fakepage(user.enterprise)
54
+ end
55
+
56
+ puts "---- Next User ----"
57
+ end
58
+
59
+ def stream_forever
60
+ while 1
61
+ stream
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Create an APP INIT request
68
+ def app_init(user)
69
+ payload = {
70
+ userId: user.id,
71
+ phoneDetails: phonedetails
72
+ }
73
+ key = apikey(user.enterprise)
74
+ url = '/events/app.init/'
75
+ post(url, key, payload)
76
+ end
77
+
78
+ # Create a page view request
79
+ def pv(user, page)
80
+ payload = {
81
+ userId: user.id,
82
+ phoneDetails: phonedetails,
83
+ routeUrl: page.routeurl,
84
+ categories: fakecategories,
85
+ tags: faketags,
86
+ }
87
+ key = apikey user.enterprise
88
+ url = '/events/page.view/'
89
+ post(url, key, payload)
90
+ end
91
+
92
+
93
+ # Create a product page view request
94
+ def ppv(user, product)
95
+ payload = {
96
+ userId: user.id,
97
+ phoneDetails: phonedetails,
98
+ routeUrl: product.routeurl,
99
+ categories: fakecategories,
100
+ tags: faketags,
101
+ productId: product.id,
102
+ productName: product.name,
103
+ price: product.price
104
+ }
105
+ key = apikey user.enterprise
106
+ url = '/events/productpage.view/'
107
+ post(url, key, payload)
108
+ end
109
+
110
+ # Hash containing phone details
111
+ # @return [Hash]
112
+ def phonedetails
113
+ {
114
+ deviceId: 'FCDBD8EF-62FC-4ECB-B2F5-92C9E79AC7F9',
115
+ manufacturer: 'Apple',
116
+ model: 'iPhone 6',
117
+ latitude: 28.6139,
118
+ longitude: 77.209
119
+ }
120
+ end
121
+
122
+ # Creates a fake user for the enterprise on the fly
123
+ # @param [Octo::Enterprise] enterprise
124
+ # @return [Octo::User]
125
+ def fakeuser(enterprise = enterprises.to_a.sample)
126
+ Octo::User.new(enterprise_id: enterprise.id,
127
+ id: rand(MAX_USERS))
128
+ end
129
+
130
+ # Generates a fake page for the enterprise
131
+ # @param [Octo::Enterprise] enterprise
132
+ # @return [Octo::Product]
133
+ def fakepage(enterprise)
134
+ id = rand(MAX_PAGES)
135
+ Octo::Page.new(
136
+ enterprise_id: enterprise.id,
137
+ routeurl: "/PageUrl/#{ id }",
138
+ categories: fakecategories,
139
+ tags: faketags
140
+ )
141
+ end
142
+
143
+
144
+ # Generates a fake product for the enterprise
145
+ # @param [Octo::Enterprise] enterprise
146
+ # @return [Octo::Product]
147
+ def fakeproduct(enterprise)
148
+ id = rand(MAX_PRODUCTS)
149
+ Octo::Product.new(
150
+ enterprise_id: enterprise.id,
151
+ id: id,
152
+ price: rand(10000).to_f,
153
+ name: "Fake Product #{ id }",
154
+ routeurl: "/Some/Url/#{ id }",
155
+ categories: fakecategories,
156
+ tags: faketags
157
+ )
158
+ end
159
+
160
+ # Generates fake categories
161
+ # @return [Array<String>] Array of fake categories
162
+ def fakecategories(count = 2)
163
+ (0..MAX_CATEGORIES).to_a.sample(count).collect { |x| "Fake Category #{ x }"}
164
+ end
165
+
166
+ # Generates fake tags
167
+ # @return [Array<String>] Array of fake tags
168
+ def faketags(count = 2)
169
+ (0..MAX_TAGS).to_a.sample(count).collect { |x| "Fake Tag #{ x }"}
170
+ end
171
+
172
+ # Fetches the APIKey for the enterprise
173
+ def apikey(enterprise)
174
+ unless @_apikeys.has_key?(enterprise.id)
175
+ @_apikeys[enterprise.id] = Octo::Authorization.find_by_username(enterprise.name).apikey
176
+ end
177
+ @_apikeys[enterprise.id]
178
+ end
179
+
180
+ # Creates the URI
181
+ def uri(url = base_url)
182
+ URI(url)
183
+ end
184
+
185
+ # Find all enterprises
186
+ # @return [Cequel::RecordSet]
187
+ def enterprises
188
+ Octo::Enterprise.all.select { |x| !x.name.nil? and x.fakedata? }
189
+ end
190
+
191
+ # Make the POST call
192
+ # @param [String] url The endpoint url
193
+ # @param [String] apikey The API Key
194
+ # @param [Hash] payload The payload
195
+ def post(url, apikey, payload)
196
+ headers = {
197
+ 'Content-Type' => 'application/json',
198
+ 'Accept' => 'application/json',
199
+ 'apikey' => apikey
200
+ }
201
+
202
+ _uri = uri(base_url + url)
203
+ req = Net::HTTP::Post.new(_uri, headers)
204
+ req.content_type = 'application/json'
205
+ req.body = payload.to_json
206
+
207
+ res = Net::HTTP.start(_uri.hostname, _uri.port) do |http|
208
+ http.request(req)
209
+ end
210
+
211
+ case res
212
+ when Net::HTTPSuccess, Net::HTTPRedirection
213
+ puts res.body
214
+ else
215
+ puts res.value
216
+ end
217
+
218
+ end
219
+
220
+ end
221
+ end
222
+
223
+ # All the main Job
224
+ def _main(config_file)
225
+
226
+ # setup signal trap
227
+ Signal.trap('INT') {
228
+ puts 'Halting on user request'
229
+ exit
230
+ }
231
+
232
+ # connect Octo and start the show
233
+ Octo.connect_with config_file
234
+ streamer = Octo::FakeStream.new
235
+ streamer.stream_forever
236
+ end
237
+
238
+ def main
239
+ config_file = if ARGV[0]
240
+ ARGV[0]
241
+ else
242
+ spec = Gem::Specification.find_by_name('octocore')
243
+ gem_root = spec.gem_dir
244
+ File.join(gem_root, 'lib', 'octocore' ,'config')
245
+ end
246
+ puts config_file
247
+ if File.exist?config_file
248
+ _main(config_file)
249
+ else
250
+ puts "Config file '#{ config_file }' does not exist. Exiting..."
251
+ end
252
+ end
253
+
254
+ main
255
+
256
+ if __FILE__ == $0
257
+ main
258
+ end
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # A simple utility to provide easy migrations, reset
4
+ # and all the rake tasks that you may need to do
5
+
6
+ # ./octocore-admin --tasks
7
+
8
+ require 'open3'
9
+
10
+ module Octo
11
+ module CliAdmin
12
+
13
+ VALID_ACTIONS = %w(init migrate reset)
14
+
15
+ module ClassMethods
16
+ def perform(action, config_dir)
17
+ if VALID_ACTIONS.include?(action)
18
+ cmd = ['rake', '-f', spec.gem_dir + '/Rakefile',
19
+ '--verbose', "octo:#{ action }",
20
+ "CONFIG_DIR=#{ config_dir }"]
21
+ #STDOUT.puts "** Executing: #{ cmd }"
22
+ Open3.popen3(*cmd) do |stdout, stderr, status, thread|
23
+ stderr.sync = true
24
+ while line=stderr.gets do
25
+ puts(line)
26
+ end
27
+ end
28
+ else
29
+ STDOUT.puts "Invalid action: #{ action }"
30
+ end
31
+ end
32
+
33
+ def spec
34
+ @spec ||= Gem::Specification.find_by_name 'octocore'
35
+ end
36
+ end
37
+
38
+ class Cli
39
+ extend ClassMethods
40
+ end
41
+
42
+ def self.main
43
+ STDOUT.sync = true
44
+ action = ARGV[0]
45
+ config_dir = ARGV[1]
46
+ Octo::CliAdmin::Cli.perform(action, config_dir)
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+
53
+ Octo::CliAdmin.main
54
+
@@ -0,0 +1,152 @@
1
+ require 'cequel'
2
+ require 'yaml'
3
+ require 'logger'
4
+
5
+ require 'octocore-cassandra/version'
6
+ require 'octocore-cassandra/config'
7
+
8
+
9
+ require 'octocore-cassandra/models'
10
+ require 'octocore-cassandra/counter'
11
+ require 'octocore-cassandra/email'
12
+ require 'octocore-cassandra/utils'
13
+
14
+ require 'octocore-cassandra/trendable'
15
+ require 'octocore-cassandra/baseline'
16
+ require 'octocore-cassandra/trends'
17
+ require 'octocore-cassandra/kldivergence'
18
+ require 'octocore-cassandra/segment'
19
+
20
+ # Mailer and scheduler should always be required in the following order
21
+ require 'octocore-cassandra/mailer'
22
+ require 'octocore-cassandra/scheduler'
23
+
24
+ require 'octocore-cassandra/message_parser'
25
+
26
+ require 'octocore-cassandra/schedeuleable'
27
+ require 'octocore-cassandra/helpers'
28
+ require 'octocore-cassandra/kafka_bridge'
29
+ require 'octocore-cassandra/stats'
30
+
31
+ # The main Octo module
32
+ module Octo
33
+
34
+ # Connect using the provided configuration. If you want to extend Octo's connect
35
+ # method you can override this method with your own. Just make sure to make
36
+ # a call to self._connect(configuration) so that Octo also connects
37
+ # @param [Hash] configuration The configuration hash
38
+ def self.connect(configuration)
39
+ self._connect(configuration)
40
+ end
41
+
42
+ # Connect by reading configuration from the provided file
43
+ # @param [String] config_file Location of the YAML config file
44
+ def self.connect_with_config_file(config_file)
45
+ config = YAML.load_file(config_file).deep_symbolize_keys
46
+ self.connect(config)
47
+ end
48
+
49
+ # Connect by reading configuration files from the given directory.
50
+ # In this case, all *.y*ml files would be read in
51
+ # Dir.glob order and merged into one unified config
52
+ def self.connect_with_config_dir(config_dir)
53
+ config = {}
54
+ accepted_formats = Set.new(['.yaml', '.yml'])
55
+ Dir[config_dir + '/*'].each do |file_obj|
56
+ if File.file?(file_obj) and accepted_formats.include?File.extname(file_obj)
57
+ config = self.true_load(config, file_obj, config_dir)
58
+ elsif File.directory?(file_obj)
59
+ Dir[file_obj + '/**/*.y*ml'].each do |file|
60
+ config = self.true_load(config, file, config_dir)
61
+ end
62
+ end
63
+ end
64
+ # As a cleanup step, merge the values of key named "config"
65
+ # with the global config hash
66
+ configConfig = config.delete(:config)
67
+ config = config.deep_merge(configConfig)
68
+ # Now, good to merge the two
69
+ self.connect config
70
+ end
71
+
72
+ # Loads the true config. The true config is the hierarchial config
73
+ # @param [Hash] config The base config. Loaded config will be deep merged
74
+ # with this
75
+ # @param [String] file The file from which config should be loaded
76
+ # @param [String] config_fir The config dir in which the file is located
77
+ # @return [Hash] The merged config hash
78
+ def self.true_load(config, file, config_dir)
79
+ _config = YAML.load_file file
80
+ if _config
81
+ $stdout.puts "Loading from Config file: #{ file }"
82
+ # A little bit of hack here.
83
+ # This hack makes sure that if we load config files from nestes
84
+ # directories, the corresponding config is loaded in
85
+ # hierarchy.
86
+ # So, if the file is like /config/search/index.yml, the
87
+ # key would be like [config[search[index]]]
88
+ a = file.gsub(/(\/?#{config_dir}(\/)*(config\/)*|.yml)/, '').split('/')
89
+ _true_config = a.reverse.inject(_config) { |r,e| { e => r } }
90
+ config = config.deep_merge(_true_config.deep_symbolize_keys)
91
+ end
92
+ config
93
+ end
94
+
95
+ # Provides a unified interface to #connect_with_config_dir
96
+ # and #connect_with_config_file for convenience
97
+ def self.connect_with(location)
98
+ if File.directory?(location)
99
+ self.connect_with_config_dir location
100
+ elsif File.file?(location)
101
+ self.connect_with_config_file location
102
+ else
103
+ puts "Invalid location #{ location }"
104
+ end
105
+ end
106
+
107
+
108
+ # A low level method to connect using a configuration
109
+ # @param [Hash] configuration The configuration hash
110
+ def self._connect(configuration)
111
+
112
+ load_config configuration
113
+
114
+ self.logger.info('Octo booting up.')
115
+
116
+ # Establish Cequel Connection
117
+ cassandra_config = Octo.get_config(:cassandra)
118
+ connection = Cequel.connect(cassandra_config)
119
+ Cequel::Record.connection = connection
120
+
121
+ # Establish connection to cache server
122
+ default_cache = {
123
+ host: '127.0.0.1', port: 6379
124
+ }
125
+ cache_config = Octo.get_config(:redis, default_cache)
126
+ Cequel::Record.update_cache_config(*cache_config.values_at(:host, :port))
127
+
128
+ # Establish connection to statsd server if required
129
+ if configuration.has_key?(:statsd)
130
+ statsd_config = configuration[:statsd]
131
+ set_config :stats, Statsd.new(*statsd_config.values)
132
+ end
133
+
134
+ self.logger.info('I\'m connected now.')
135
+ require 'octocore-cassandra/callbacks'
136
+
137
+ self.logger.info('Setting callbacks.')
138
+
139
+ Octo::Callbacks.run_hook(:after_connect)
140
+ end
141
+
142
+ # Creates a logger for Octo
143
+ def self.logger
144
+ unless @logger
145
+ @logger = Logger.new(Octo.get_config(:logfile, $stdout)).tap do |log|
146
+ log.progname = name
147
+ end
148
+ end
149
+ @logger
150
+ end
151
+
152
+ end