cellect-server 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.
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