cellect-server 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +2 -0
  5. data/README.md +19 -0
  6. data/Rakefile +9 -0
  7. data/cellect-server.gemspec +40 -0
  8. data/cellect.gemspec +33 -0
  9. data/lib/cellect.rb +7 -0
  10. data/lib/cellect/node_set.rb +38 -0
  11. data/lib/cellect/server.rb +30 -0
  12. data/lib/cellect/server/adapters.rb +13 -0
  13. data/lib/cellect/server/adapters/default.rb +60 -0
  14. data/lib/cellect/server/adapters/postgres.rb +64 -0
  15. data/lib/cellect/server/api.rb +48 -0
  16. data/lib/cellect/server/api/helpers.rb +44 -0
  17. data/lib/cellect/server/api/sets.rb +21 -0
  18. data/lib/cellect/server/api/users.rb +32 -0
  19. data/lib/cellect/server/grouped_project.rb +65 -0
  20. data/lib/cellect/server/node_set.rb +19 -0
  21. data/lib/cellect/server/project.rb +123 -0
  22. data/lib/cellect/server/user.rb +66 -0
  23. data/lib/cellect/version.rb +3 -0
  24. data/spec/fixtures/project_data/grouped_pairwise_priority.json +109 -0
  25. data/spec/fixtures/project_data/grouped_pairwise_random.json +89 -0
  26. data/spec/fixtures/project_data/grouped_priority.json +59 -0
  27. data/spec/fixtures/project_data/grouped_random.json +49 -0
  28. data/spec/fixtures/project_data/pairwise_priority.json +49 -0
  29. data/spec/fixtures/project_data/pairwise_random.json +39 -0
  30. data/spec/fixtures/project_data/priority.json +49 -0
  31. data/spec/fixtures/project_data/random.json +39 -0
  32. data/spec/fixtures/user_data/complete_user.json +118 -0
  33. data/spec/fixtures/user_data/new_user.json +26 -0
  34. data/spec/fixtures/user_data/partial_user.json +58 -0
  35. data/spec/server/api/add_seen_spec.rb +26 -0
  36. data/spec/server/api/add_spec.rb +40 -0
  37. data/spec/server/api/remove_spec.rb +35 -0
  38. data/spec/server/api/sample_spec.rb +34 -0
  39. data/spec/server/api/user_load_spec.rb +25 -0
  40. data/spec/server/grouped_project_spec.rb +76 -0
  41. data/spec/server/node_set_spec.rb +13 -0
  42. data/spec/server/project_spec.rb +62 -0
  43. data/spec/server/server_spec.rb +24 -0
  44. data/spec/server/user_spec.rb +32 -0
  45. data/spec/spec_helper.rb +43 -0
  46. data/spec/support/cellect_helper.rb +12 -0
  47. data/spec/support/shared_api_context.rb +11 -0
  48. data/spec/support/shared_examples_for_node_set.rb +27 -0
  49. data/spec/support/shared_examples_for_project.rb +26 -0
  50. data/spec/support/shared_examples_for_set.rb +34 -0
  51. data/spec/support/spec_adapter.rb +43 -0
  52. data/spec/support/zk_setup.rb +26 -0
  53. metadata +337 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3b7f28e9a20fa75b575fe23b06eb1690f1c78943
4
+ data.tar.gz: ece4c409106d6bdba0d50e9fead70d0c1ce399d9
5
+ SHA512:
6
+ metadata.gz: 439ca8ce160576d91933308d882ce74c131d926d8b9e1394e45e37b532f0c36ca122b79e9e4625508712b89eee2cddc4654d55f694a71f21aa0cfcd23837c579
7
+ data.tar.gz: 90b0afb4aa5ab74af30c3094fb8e103b507ab96c8ad2dbe6b5119844ddbea2b0f451e8b7cfd386373d1959e43c6dd5339b53ec5b2e118a499caf63edcf23e561
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ .bundle
2
+ .DS_Store
3
+ .rvmrc
4
+ .ruby-version
5
+ /data/*
6
+ /ext/*.o
7
+ /ext/Makefile
8
+ /ext/*.bundle
9
+ /scratch
10
+ *.so
11
+ .vagrant
12
+ log/*
13
+ tmp/*
14
+ *.gem
15
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec name: 'cellect'
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # Cellect
2
+
3
+ This is a work in progress
4
+
5
+ ## Building
6
+
7
+ 1. Install [Boost](http://www.boost.org/): OS X: `brew update && brew install boost`, Ubuntu: `sudo apt-get update && sudo apt-get install libboost-all-dev`
8
+ 2. Install gem dependencies: `bundle` (See Note)
9
+ 3. Build extension: `cd ext; ruby extconf.rb; make; cd ..`
10
+
11
+ ### Note
12
+ To install rice your Ruby must be compiled with shared libraries enabled, from the rice docs:
13
+ * rvm: `rvm reinstall [version] -- --enable-shared`
14
+ * rbenv: `CONFIGURE_OPTS="--enable-shared" rbenv install [version]`
15
+
16
+
17
+ ## Testing
18
+
19
+ Run the specs with `rake`
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_helper'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Bundler::GemHelper.install_tasks name: 'cellect'
5
+ Bundler::GemHelper.install_tasks name: 'cellect-server'
6
+ Bundler::GemHelper.install_tasks name: 'cellect-client'
7
+
8
+ RSpec::Core::RakeTask.new :spec
9
+ task default: :spec
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cellect/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'cellect-server'
8
+ spec.version = Cellect::VERSION
9
+ spec.authors = ['Michael Parrish']
10
+ spec.email = ['michael@zooniverse.org']
11
+ spec.summary = ''
12
+ spec.description = ''
13
+ spec.homepage = 'https://github.com/parrish/Cellect'
14
+ spec.license = 'MIT'
15
+
16
+ ignored_paths = %w(config data log script tmp).collect{ |path| Dir["#{ path }/**/*"] }.flatten
17
+ ignored_files = %w(Dockerfile Vagrantfile Gemfile.lock config.ru) + ignored_paths
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject{ |f| f =~ /client/ } - ignored_files
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.5'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'oj'
27
+ spec.add_development_dependency 'rspec'
28
+ spec.add_development_dependency 'rack-test'
29
+ spec.add_development_dependency 'pry'
30
+ spec.add_development_dependency 'puma', '~> 2.8'
31
+ spec.add_development_dependency 'pg', '~> 0.17'
32
+ spec.add_development_dependency 'connection_pool', '~> 2.0'
33
+
34
+ spec.add_runtime_dependency 'diff_set'
35
+ spec.add_runtime_dependency 'celluloid', '0.16.0.pre'
36
+ spec.add_runtime_dependency 'celluloid-io', '0.16.0.pre'
37
+ spec.add_runtime_dependency 'http', '~> 0.6'
38
+ spec.add_runtime_dependency 'zk', '~> 1.9'
39
+ spec.add_runtime_dependency 'grape', '~> 0.7'
40
+ end
data/cellect.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cellect/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'cellect'
8
+ spec.version = Cellect::VERSION
9
+ spec.authors = ['Michael Parrish']
10
+ spec.email = ['michael@zooniverse.org']
11
+ spec.summary = ''
12
+ spec.description = ''
13
+ spec.homepage = 'https://github.com/parrish/Cellect'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = ['lib/cellect.rb', 'lib/cellect/version.rb']
17
+ spec.executables = []
18
+ spec.test_files = []
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.5'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'oj'
24
+ spec.add_development_dependency 'rspec'
25
+ spec.add_development_dependency 'rack-test'
26
+ spec.add_development_dependency 'pry'
27
+ spec.add_development_dependency 'puma', '~> 2.8'
28
+ spec.add_development_dependency 'pg', '~> 0.17'
29
+ spec.add_development_dependency 'connection_pool', '~> 2.0'
30
+
31
+ spec.add_runtime_dependency 'cellect-server', Cellect::VERSION
32
+ spec.add_runtime_dependency 'cellect-client', Cellect::VERSION
33
+ end
data/lib/cellect.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'celluloid'
2
+ require 'celluloid/io'
3
+ require 'cellect/version'
4
+
5
+ module Cellect
6
+
7
+ end
@@ -0,0 +1,38 @@
1
+ require 'zk'
2
+ require 'timeout'
3
+
4
+ module Cellect
5
+ class NodeSet
6
+ include Celluloid
7
+
8
+ attr_accessor :zk, :state
9
+
10
+ def initialize
11
+ self.state = :initializing
12
+ after(0.001){ async.initialize_zk } # don't block waiting for ZK to connect
13
+ end
14
+
15
+ def initialize_zk
16
+ # don't let ZK hang the thread, just retry connection on restart
17
+ Timeout::timeout(5) do
18
+ self.zk = ZK.new zk_url, chroot: '/cellect'
19
+ end
20
+ setup
21
+ self.state = :ready
22
+ end
23
+
24
+ def ready?
25
+ state == :ready
26
+ end
27
+
28
+ protected
29
+
30
+ def zk_url
31
+ ENV.fetch 'ZK_URL', 'localhost:2181'
32
+ end
33
+
34
+ def setup
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ require 'diff_set'
2
+ require 'cellect'
3
+ require 'celluloid/autostart'
4
+
5
+ module Cellect
6
+ module Server
7
+ require 'cellect/server/node_set'
8
+ require 'cellect/server/adapters'
9
+ require 'cellect/server/project'
10
+ require 'cellect/server/grouped_project'
11
+ require 'cellect/server/user'
12
+ require 'cellect/server/api'
13
+
14
+ class << self
15
+ attr_accessor :node_set
16
+ end
17
+
18
+ def self.ready?
19
+ Project.all.each do |project|
20
+ return false unless project.ready?
21
+ end
22
+
23
+ true
24
+ rescue
25
+ false
26
+ end
27
+
28
+ Server.node_set = NodeSet.supervise
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module Cellect
2
+ module Server
3
+ class << self
4
+ attr_accessor :adapter
5
+ end
6
+
7
+ module Adapters
8
+ require 'cellect/server/adapters/default'
9
+ end
10
+
11
+ Cellect::Server.adapter = Adapters::Default.new
12
+ end
13
+ end
@@ -0,0 +1,60 @@
1
+ module Cellect
2
+ module Server
3
+ module Adapters
4
+ class Default
5
+ # Return a list of projects to load in the form:
6
+ # [{
7
+ # 'id' => 123,
8
+ # 'name' => 'foo',
9
+ # 'prioritized' => false,
10
+ # 'pairwise' => false,
11
+ # 'grouped' => false
12
+ # }, ...]
13
+ def project_list
14
+ raise NotImplementedError
15
+ end
16
+
17
+ # Load the data for a project, this method:
18
+ # Accepts a project
19
+ # Returns an array of hashes in the form:
20
+ # {
21
+ # 'id' => 123,
22
+ # 'priority' => 0.123,
23
+ # 'group_id' => 456
24
+ # }
25
+ def load_data_for(project_name)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ # Load seen ids for a user, this method:
30
+ # Accepts a project_name, and a user id
31
+ # Returns an array in the form:
32
+ # [1, 2, 3]
33
+ def load_user(project_name, id)
34
+ raise NotImplementedError
35
+ end
36
+
37
+ def load_projects
38
+ project_list.each{ |project_info| load_project project_info }
39
+ end
40
+
41
+ def load_project(args)
42
+ info = if args.is_a?(Hash)
43
+ args
44
+ elsif args.is_a?(String)
45
+ project_list.select{ |h| h['name'] == args }.first
46
+ else
47
+ raise ArgumentError
48
+ end
49
+
50
+ project_for info
51
+ end
52
+
53
+ def project_for(opts = { })
54
+ project_klass = opts.fetch('grouped', false) ? GroupedProject : Project
55
+ project_klass[opts['name'], pairwise: opts['pairwise'], prioritized: opts['prioritized']]
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,64 @@
1
+ require 'pg'
2
+ require 'connection_pool'
3
+
4
+ module Cellect
5
+ module Server
6
+ module Adapters
7
+ class Postgres < Default
8
+ def initialize
9
+ @pg ||= ConnectionPool.new(size: ENV.fetch('PG_POOL_SIZE', 25).to_i) do
10
+ PG.connect connection_options
11
+ end
12
+ end
13
+
14
+ def project_list
15
+ with_pg do |pg|
16
+ pg.exec('select * from projects').collect do |row|
17
+ {
18
+ 'id' => row['id'].to_i,
19
+ 'name' => row['id'],
20
+ 'prioritized' => row['prioritized'] == 't',
21
+ 'pairwise' => row['pairwise'] == 't',
22
+ 'grouped' => row['grouped'] == 't'
23
+ }
24
+ end
25
+ end
26
+ end
27
+
28
+ def load_data_for(project_name)
29
+ with_pg do |pg|
30
+ pg.exec("select id, priority, group_id from project_#{ project_name }_subjects").collect do |row|
31
+ {
32
+ 'id' => row['id'].to_i,
33
+ 'priority' => row['priority'].to_f,
34
+ 'group_id' => row['group_id'].to_i
35
+ }
36
+ end
37
+ end
38
+ end
39
+
40
+ def load_user(project_name, id)
41
+ with_pg do |pg|
42
+ pg.exec("select subject_id from project_#{ project_name }_classifications where user_id=#{ id }").collect do |row|
43
+ row['subject_id'].to_i
44
+ end
45
+ end
46
+ end
47
+
48
+ def with_pg
49
+ @pg.with{ |pg| yield pg }
50
+ end
51
+
52
+ def connection_options
53
+ {
54
+ host: ENV.fetch('PG_HOST', '127.0.0.1'),
55
+ port: ENV.fetch('PG_PORT', '5432'),
56
+ dbname: ENV.fetch('PG_DB', 'cellect'),
57
+ user: ENV.fetch('PG_USER', 'cellect'),
58
+ password: ENV.fetch('PG_PASS', '')
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,48 @@
1
+ require 'grape'
2
+
3
+ module Cellect
4
+ module Server
5
+ class API < Grape::API
6
+ format :json
7
+
8
+ require 'cellect/server/api/helpers'
9
+ require 'cellect/server/api/sets'
10
+ require 'cellect/server/api/users'
11
+
12
+ get :stats do
13
+ usage = ->(keyword) do
14
+ `ps axo #{ keyword }`.chomp.split("\n")[1..-1].collect(&:to_f).inject :+
15
+ end
16
+
17
+ {
18
+ memory: usage.call('%mem'),
19
+ cpu: usage.call('%cpu')
20
+ }
21
+ end
22
+
23
+ resources :projects do
24
+ get do
25
+ Cellect::Server.adapter.project_list
26
+ end
27
+
28
+ segment '/:project_id' do
29
+ helpers Helpers
30
+ mount Sets
31
+ mount Users
32
+
33
+ get :status do
34
+ project.status
35
+ end
36
+
37
+ post :reload do
38
+ project.async.load_data
39
+ end
40
+
41
+ delete do
42
+ # delete a project (maybe?)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ module Cellect
2
+ module Server
3
+ class API
4
+ module Helpers
5
+ def project
6
+ @project ||= Project[params[:project_id]]
7
+ end
8
+
9
+ def selector_params
10
+ {
11
+ limit: param_to_int(:limit, default: 5),
12
+ user_id: param_to_int(:user_id),
13
+ group_id: param_to_int(:group_id)
14
+ }
15
+ end
16
+
17
+ def update_params
18
+ {
19
+ subject_id: param_to_int(:subject_id),
20
+ group_id: param_to_int(:group_id),
21
+ priority: param_to_float(:priority)
22
+ }
23
+ end
24
+
25
+ def param_to_int(param, default: nil)
26
+ _param_to param, :to_i, default
27
+ end
28
+
29
+ def param_to_float(param, default: nil)
30
+ _param_to param, :to_f, default
31
+ end
32
+
33
+ def _param_to(param, conversion, default)
34
+ val = params[param].try conversion
35
+ params[param] && val && val > 0 ? val : default
36
+ end
37
+
38
+ def to_query(hash)
39
+ hash.select{ |k, v| v }.collect{ |k, v| "#{ k }=#{ v }" }.join '&'
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end