cellect-server 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|