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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/Gemfile +2 -0
- data/README.md +19 -0
- data/Rakefile +9 -0
- data/cellect-server.gemspec +40 -0
- data/cellect.gemspec +33 -0
- data/lib/cellect.rb +7 -0
- data/lib/cellect/node_set.rb +38 -0
- data/lib/cellect/server.rb +30 -0
- data/lib/cellect/server/adapters.rb +13 -0
- data/lib/cellect/server/adapters/default.rb +60 -0
- data/lib/cellect/server/adapters/postgres.rb +64 -0
- data/lib/cellect/server/api.rb +48 -0
- data/lib/cellect/server/api/helpers.rb +44 -0
- data/lib/cellect/server/api/sets.rb +21 -0
- data/lib/cellect/server/api/users.rb +32 -0
- data/lib/cellect/server/grouped_project.rb +65 -0
- data/lib/cellect/server/node_set.rb +19 -0
- data/lib/cellect/server/project.rb +123 -0
- data/lib/cellect/server/user.rb +66 -0
- data/lib/cellect/version.rb +3 -0
- data/spec/fixtures/project_data/grouped_pairwise_priority.json +109 -0
- data/spec/fixtures/project_data/grouped_pairwise_random.json +89 -0
- data/spec/fixtures/project_data/grouped_priority.json +59 -0
- data/spec/fixtures/project_data/grouped_random.json +49 -0
- data/spec/fixtures/project_data/pairwise_priority.json +49 -0
- data/spec/fixtures/project_data/pairwise_random.json +39 -0
- data/spec/fixtures/project_data/priority.json +49 -0
- data/spec/fixtures/project_data/random.json +39 -0
- data/spec/fixtures/user_data/complete_user.json +118 -0
- data/spec/fixtures/user_data/new_user.json +26 -0
- data/spec/fixtures/user_data/partial_user.json +58 -0
- data/spec/server/api/add_seen_spec.rb +26 -0
- data/spec/server/api/add_spec.rb +40 -0
- data/spec/server/api/remove_spec.rb +35 -0
- data/spec/server/api/sample_spec.rb +34 -0
- data/spec/server/api/user_load_spec.rb +25 -0
- data/spec/server/grouped_project_spec.rb +76 -0
- data/spec/server/node_set_spec.rb +13 -0
- data/spec/server/project_spec.rb +62 -0
- data/spec/server/server_spec.rb +24 -0
- data/spec/server/user_spec.rb +32 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/cellect_helper.rb +12 -0
- data/spec/support/shared_api_context.rb +11 -0
- data/spec/support/shared_examples_for_node_set.rb +27 -0
- data/spec/support/shared_examples_for_project.rb +26 -0
- data/spec/support/shared_examples_for_set.rb +34 -0
- data/spec/support/spec_adapter.rb +43 -0
- data/spec/support/zk_setup.rb +26 -0
- 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
data/.rspec
ADDED
data/Gemfile
ADDED
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,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,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
|