cabalist 0.0.1 → 0.0.2
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.
- data/.gitignore +4 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE +0 -0
- data/README.md +121 -0
- data/Rakefile +17 -0
- data/cabalist.gemspec +42 -0
- data/lib/cabalist.rb +13 -2
- data/lib/cabalist/configuration.rb +20 -0
- data/lib/cabalist/frontend.rb +111 -0
- data/lib/cabalist/model_additions.rb +127 -0
- data/lib/cabalist/railtie.rb +14 -0
- data/lib/cabalist/version.rb +3 -0
- data/lib/cabalist/views/classifier.haml +82 -0
- data/lib/cabalist/views/index.haml +21 -0
- data/lib/cabalist/views/layout.haml +39 -0
- data/lib/generators/cabalist/classifier/USAGE +0 -0
- data/lib/generators/cabalist/classifier/classifier_generator.rb +37 -0
- data/lib/generators/cabalist/classifier/templates/migrations/add_cabalist.rb.erb +11 -0
- data/lib/generators/cabalist/install/USAGE +0 -0
- data/lib/generators/cabalist/install/install_generator.rb +26 -0
- data/lib/generators/cabalist/install/templates/initializers/cabalist.rb +4 -0
- data/lib/generators/cabalist/install/templates/public/images/eye_12x9.png +0 -0
- data/lib/generators/cabalist/install/templates/public/images/logo.png +0 -0
- data/lib/generators/cabalist/install/templates/public/images/ncirl.png +0 -0
- data/lib/generators/cabalist/install/templates/public/images/pen_12x12.png +0 -0
- data/lib/generators/cabalist/install/templates/public/images/symbol.png +0 -0
- data/lib/generators/cabalist/install/templates/public/images/target_12x12.png +0 -0
- data/lib/generators/cabalist/install/templates/public/images/x_14x14.png +0 -0
- data/lib/generators/cabalist/install/templates/public/javascripts/cabalist.js +23 -0
- data/lib/generators/cabalist/install/templates/public/stylesheets/cabalist.css +197 -0
- data/spec/cabalist/frontend_spec.rb +79 -0
- data/spec/cabalist/model_additions_spec.rb +104 -0
- data/spec/cabalist/performance_benchmark_spec.rb +76 -0
- data/spec/spec_helper.rb +30 -0
- metadata +164 -12
- data/lib/cabalist/manager.rb +0 -30
- data/lib/cabalist/object_hooks.rb +0 -59
@@ -0,0 +1,82 @@
|
|
1
|
+
.content
|
2
|
+
|
3
|
+
%ul.scopes
|
4
|
+
%lh= "#{klass} classifier:"
|
5
|
+
%li
|
6
|
+
%a{ :href => "/cabalist/#{klass.to_s.downcase}/all" } All
|
7
|
+
%li
|
8
|
+
%a{ :href => "/cabalist/#{klass.to_s.downcase}/none" } Not Classified
|
9
|
+
%li
|
10
|
+
%a{ :href => "/cabalist/#{klass.to_s.downcase}/manual" } Manually Classified
|
11
|
+
%li
|
12
|
+
%a{ :href => "/cabalist/#{klass.to_s.downcase}/auto" } Automatically Classified
|
13
|
+
|
14
|
+
- unless @collection.empty?
|
15
|
+
%table.cabalist_records
|
16
|
+
%thead
|
17
|
+
%tr
|
18
|
+
%td ID
|
19
|
+
- klass.classifier.data_set.data_labels.each do |l|
|
20
|
+
%td= l.to_s.humanize.titlecase
|
21
|
+
%td.center Actions
|
22
|
+
%tbody
|
23
|
+
- @collection.each do |el|
|
24
|
+
%tr
|
25
|
+
%td= el.id
|
26
|
+
- el.get_features.each do |feature|
|
27
|
+
%td= feature.to_s
|
28
|
+
%td= el.get_class_variable.to_s
|
29
|
+
%td.center
|
30
|
+
|
31
|
+
%img{ :src => '/images/eye_12x9.png',
|
32
|
+
:class => 'show_icon',
|
33
|
+
:title => 'Show classified object' }/
|
34
|
+
.overlay.show_widget
|
35
|
+
.dialog-widget
|
36
|
+
.dialog-content
|
37
|
+
%img{ :src => '/images/x_14x14.png', :class => 'close_icon' }/
|
38
|
+
%pre= el.attributes.to_yaml
|
39
|
+
|
40
|
+
%img{ :src => '/images/pen_12x12.png',
|
41
|
+
:class => 'edit_icon',
|
42
|
+
:title => 'Manually classify' }/
|
43
|
+
.overlay.edit_widget
|
44
|
+
.dialog-widget
|
45
|
+
.dialog-content
|
46
|
+
%img{ :src => '/images/x_14x14.png', :class => 'close_icon' }/
|
47
|
+
|
48
|
+
-# This form is for manual classification of Cabalist objects. --------- #
|
49
|
+
%form{ :action => "/cabalist/#{klass.to_s.downcase}/teach/#{el.id}",
|
50
|
+
:method => 'POST' }
|
51
|
+
%p.label= "#{el.class.get_class_variable_name.to_s.humanize.titlecase}:"
|
52
|
+
%span.label_info Use existing class:
|
53
|
+
%select{ :name => 'classification' }
|
54
|
+
%option{ :value => nil } - choose classification -
|
55
|
+
- el.class.classifier.data_set.build_domain(-1).to_a.each do |opt|
|
56
|
+
%option{ :value => opt.to_s,
|
57
|
+
:selected => (el.get_class_variable == opt) }= opt
|
58
|
+
%p
|
59
|
+
%span.label_info ...or add a new one:
|
60
|
+
%input{ :name => 'classification_freeform', :value => '' }/
|
61
|
+
%p
|
62
|
+
%input{ :type => 'submit', :value => 'Teach!' }/
|
63
|
+
|
64
|
+
-# This is the for for autoclassification of Cabalist objects. --------------- #
|
65
|
+
%form{ :action => "/cabalist/#{klass.to_s.downcase}/autoclassify/#{el.id}",
|
66
|
+
:method => 'POST', :class => 'autoclassify' }
|
67
|
+
%input{ :type => 'image',
|
68
|
+
:src => '/images/target_12x12.png',
|
69
|
+
:title => "Autoclassify (#{el.classify.to_s})" }/
|
70
|
+
|
71
|
+
|
72
|
+
%table.kaminari_nav
|
73
|
+
%tr
|
74
|
+
%td.previous
|
75
|
+
- if page.to_i > 1
|
76
|
+
%a{ :href => "/cabalist/#{klass.to_s.downcase}/#{scope}/#{page-1}" } « Previous
|
77
|
+
%td.next
|
78
|
+
- if (page * Cabalist::Frontend::PER_PAGE) < total
|
79
|
+
%a{ :href => "/cabalist/#{klass.to_s.downcase}/#{scope}/#{page+1}" } Next »
|
80
|
+
|
81
|
+
- else
|
82
|
+
%p No data here yet.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
.content
|
2
|
+
|
3
|
+
%h1 Dashboard
|
4
|
+
|
5
|
+
%ul.classifiers
|
6
|
+
- @classes.each do |klass|
|
7
|
+
%li
|
8
|
+
%ul.specs
|
9
|
+
%lh
|
10
|
+
= klass.to_s
|
11
|
+
classifier
|
12
|
+
%li
|
13
|
+
Classifies the
|
14
|
+
%span.attr_name= klass.classifier.data_set.data_labels[-1]
|
15
|
+
attribute
|
16
|
+
%li
|
17
|
+
= klass.classifier.data_set.data_items.length
|
18
|
+
data points
|
19
|
+
%li
|
20
|
+
Signals:
|
21
|
+
%span.attr_name= klass.classifier.data_set.data_labels[0...-1].join(', ')
|
@@ -0,0 +1,39 @@
|
|
1
|
+
!!! 5
|
2
|
+
|
3
|
+
%html
|
4
|
+
%head
|
5
|
+
%title= "Cabalist | #{@app_name}"
|
6
|
+
%link{ :href => '/stylesheets/cabalist.css',
|
7
|
+
:rel => 'stylesheet',
|
8
|
+
:type => 'text/css' }/
|
9
|
+
%script{ :src => '/assets/jquery.js',
|
10
|
+
:type => 'text/javascript' }
|
11
|
+
%script{ :src => '/javascripts/cabalist.js',
|
12
|
+
:type => 'text/javascript' }
|
13
|
+
%body
|
14
|
+
%header
|
15
|
+
.container
|
16
|
+
%a.logo{ :href => '/cabalist' }
|
17
|
+
%img{ :src => '/images/logo.png' }/
|
18
|
+
%nav
|
19
|
+
- @classes.each do |klass|
|
20
|
+
%a{ :href => "/cabalist/#{klass.to_s.downcase}/all" }= klass.to_s.pluralize
|
21
|
+
|
22
|
+
= yield
|
23
|
+
|
24
|
+
%footer
|
25
|
+
%p
|
26
|
+
Cabalist is an open source Ruby library developed by
|
27
|
+
%a{ :href => 'http://github.com/marcinwyszynski/' } Marcin Wyszynski.
|
28
|
+
AI algorithms come from
|
29
|
+
%a{ :href => 'http://ai4r.rubyforge.org/' } ai4r
|
30
|
+
gem. Layout shamesly stolen from
|
31
|
+
= succeed '.' do
|
32
|
+
%a{ :href => 'http://redis.io/' } redis.io
|
33
|
+
More credits available
|
34
|
+
= succeed '.' do
|
35
|
+
%a{ :href => '#' } here
|
36
|
+
.ncirl
|
37
|
+
%span National College of Ireland
|
38
|
+
%a{ :href => 'http://www.ncirl.ie/' }
|
39
|
+
%img{ :alt => 'National College of Ireland', :src => '/images/ncirl.png' }/
|
File without changes
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Cabalist
|
2
|
+
class ClassifierGenerator < Rails::Generators::NamedBase
|
3
|
+
desc <<-EOF
|
4
|
+
This generator creates a migration to add autoclassified_at attribute
|
5
|
+
to a chosen ActiveRecord model. The model name should be supplied as
|
6
|
+
and attrubute to the generator:
|
7
|
+
rails generate cabalist:classifier <ModelName>
|
8
|
+
EOF
|
9
|
+
|
10
|
+
include Rails::Generators::Migration
|
11
|
+
|
12
|
+
source_root File.expand_path('../templates/', __FILE__)
|
13
|
+
argument :name, :type => :string
|
14
|
+
|
15
|
+
def self.next_migration_number(dirname)
|
16
|
+
Time.now.strftime("%Y%m%d%H%M%S")
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_migration
|
20
|
+
migration_template("migrations/add_cabalist.rb.erb",
|
21
|
+
"db/migrate/add_cabalist_to_#{name.tableize}.rb")
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_to_gui_if_necessary
|
25
|
+
add_to_gui = ''
|
26
|
+
until %w(y Y n N).include?(add_to_gui)
|
27
|
+
add_to_gui = ask "Would you like to have access to this model through GUI dashboard? (Y/n) "
|
28
|
+
end
|
29
|
+
if %w(Y y).include?(add_to_gui)
|
30
|
+
inject_into_file "config/initializers/cabalist.rb",
|
31
|
+
"\n c.frontend_classes << #{name}",
|
32
|
+
:before => "\nend"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Cabalist
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
desc <<-EOF
|
4
|
+
This generator installs the Cabalist initializer and
|
5
|
+
adds the route to Cabalist GUI (Cabalist::Frontend).
|
6
|
+
Usage:
|
7
|
+
rails generate cabalist:install <ModelName>
|
8
|
+
EOF
|
9
|
+
|
10
|
+
source_root File.expand_path('../templates/', __FILE__)
|
11
|
+
|
12
|
+
def copy_initializer
|
13
|
+
copy_file "initializers/cabalist.rb",
|
14
|
+
"config/initializers/cabalist.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
def copy_assets
|
18
|
+
directory "public/", "public/"
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_route
|
22
|
+
route "match '/cabalist' => Cabalist::Frontend, :anchor => false, :as => :cabalist"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,23 @@
|
|
1
|
+
$(document).ready(function() {
|
2
|
+
|
3
|
+
$('img.show_icon').click(function() {
|
4
|
+
widget = $(this).parent().children('div.show_widget');
|
5
|
+
widget.css({ display: 'block' });
|
6
|
+
$(document).one('keydown', function(e) {
|
7
|
+
if (e.which == 27) widget.css({ display: 'none' });
|
8
|
+
});
|
9
|
+
});
|
10
|
+
|
11
|
+
$('img.edit_icon').click(function() {
|
12
|
+
widget = $(this).parent().children('div.edit_widget');
|
13
|
+
widget.css({ display: 'block' });
|
14
|
+
$(document).one('keydown', function(e) {
|
15
|
+
if (e.which == 27) widget.css({ display: 'none' });
|
16
|
+
});
|
17
|
+
});
|
18
|
+
|
19
|
+
$('img.close_icon').click(function() {
|
20
|
+
$(this).closest('div.overlay').css({ display: 'none' })
|
21
|
+
});
|
22
|
+
|
23
|
+
});
|
@@ -0,0 +1,197 @@
|
|
1
|
+
body {
|
2
|
+
background: #DDD;
|
3
|
+
font-family: "Helvetica Neue", sans-serif;
|
4
|
+
margin: 43px 0px 0px;
|
5
|
+
}
|
6
|
+
|
7
|
+
a { text-decoration: none; }
|
8
|
+
a:hover { text-decoration: underline; }
|
9
|
+
|
10
|
+
header {
|
11
|
+
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #323232), color-stop(100%, #000000));
|
12
|
+
box-shadow: #999999 0px 4px 8px 0;
|
13
|
+
position: fixed;
|
14
|
+
width: 100%;
|
15
|
+
height: 43px;
|
16
|
+
top: 0;
|
17
|
+
left: 0;
|
18
|
+
padding: 0px 10px;
|
19
|
+
z-index: 1000;
|
20
|
+
}
|
21
|
+
|
22
|
+
header nav {
|
23
|
+
float: left;
|
24
|
+
border-left: 1px solid #323232;
|
25
|
+
padding: 4px 0px 4px 20px;
|
26
|
+
}
|
27
|
+
header img {
|
28
|
+
float: left;
|
29
|
+
padding: 4px 20px 4px 0px;
|
30
|
+
border-right: 1px solid black;
|
31
|
+
}
|
32
|
+
nav a {
|
33
|
+
font-size: 12px;
|
34
|
+
font-weight: bold;
|
35
|
+
line-height: 35px;
|
36
|
+
color: #ddd;
|
37
|
+
margin: 4px 0px;
|
38
|
+
padding-right: 20px;
|
39
|
+
text-shadow: black 0px 1px 1px;
|
40
|
+
}
|
41
|
+
header a:hover { color: white; }
|
42
|
+
|
43
|
+
.container {
|
44
|
+
margin: 0 auto;
|
45
|
+
max-width: 900px;
|
46
|
+
}
|
47
|
+
h1 { margin: .3em 0 .6em; }
|
48
|
+
|
49
|
+
.content {
|
50
|
+
background-color: #FAFAFA;
|
51
|
+
box-shadow: #cccccc 0px 0px 10px 0;
|
52
|
+
color: #333;
|
53
|
+
margin: 0 auto;
|
54
|
+
max-width: 880px;
|
55
|
+
padding: 10px 10px;
|
56
|
+
}
|
57
|
+
|
58
|
+
footer {
|
59
|
+
background: url(../images/symbol.png) no-repeat left top;
|
60
|
+
padding: 10px 10px 10px 62px;
|
61
|
+
max-width: 840px;
|
62
|
+
margin: 5px auto;
|
63
|
+
color: #777;
|
64
|
+
position: relative;
|
65
|
+
font-size: 11px;
|
66
|
+
}
|
67
|
+
footer a { color: rgb(110, 180, 30); }
|
68
|
+
footer p { width: 400px; margin-top: 0px; }
|
69
|
+
.ncirl {
|
70
|
+
position: absolute;
|
71
|
+
top: 0;
|
72
|
+
right: 0;
|
73
|
+
padding: 10px 10px;
|
74
|
+
}
|
75
|
+
.ncirl span { float: right; }
|
76
|
+
.ncirl img { float: right; clear: right; }
|
77
|
+
|
78
|
+
table.cabalist_records {
|
79
|
+
width: 100%;
|
80
|
+
border-collapse: collapse;
|
81
|
+
}
|
82
|
+
|
83
|
+
table.cabalist_records td {
|
84
|
+
border: 1px solid #CCC;
|
85
|
+
font-size: 80%;
|
86
|
+
padding: 0.4em 0.6em;
|
87
|
+
}
|
88
|
+
|
89
|
+
table.cabalist_records tr:nth-of-type(even) td {
|
90
|
+
background: #EEE;
|
91
|
+
}
|
92
|
+
|
93
|
+
table.cabalist_records thead td {
|
94
|
+
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #DEDEDE), color-stop(100%, #CCC));
|
95
|
+
font-weight: bold;
|
96
|
+
text-shadow: white 0px 1px 1px;
|
97
|
+
}
|
98
|
+
|
99
|
+
ul.scopes {
|
100
|
+
background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #DEDEDE), color-stop(100%, #AAA));
|
101
|
+
font-size: 90%;
|
102
|
+
text-shadow: white 0px 1px 1px;
|
103
|
+
list-style-type: none;
|
104
|
+
margin: -10px -10px 10px -10px;
|
105
|
+
padding: 5px;
|
106
|
+
}
|
107
|
+
|
108
|
+
ul.scopes lh {
|
109
|
+
font-weight: bold;
|
110
|
+
}
|
111
|
+
|
112
|
+
ul.scopes li {
|
113
|
+
display: inline-block;
|
114
|
+
margin-left: 12px;
|
115
|
+
}
|
116
|
+
|
117
|
+
ul.scopes li a {
|
118
|
+
color: #333;
|
119
|
+
}
|
120
|
+
|
121
|
+
ul.scopes li a:hover {
|
122
|
+
color: white;
|
123
|
+
text-decoration: none;
|
124
|
+
text-shadow: #888 0px 1px 1px;
|
125
|
+
}
|
126
|
+
|
127
|
+
table.kaminari_nav {
|
128
|
+
width: 100%;
|
129
|
+
font-size: 80%;
|
130
|
+
}
|
131
|
+
|
132
|
+
table.kaminari_nav .next {
|
133
|
+
text-align: right;
|
134
|
+
}
|
135
|
+
|
136
|
+
pre {
|
137
|
+
margin: 0;
|
138
|
+
white-space: pre-wrap; /* css-3 */
|
139
|
+
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
140
|
+
white-space: -pre-wrap; /* Opera 4-6 */
|
141
|
+
white-space: -o-pre-wrap; /* Opera 7 */
|
142
|
+
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
143
|
+
}
|
144
|
+
|
145
|
+
.center {
|
146
|
+
text-align: center;
|
147
|
+
}
|
148
|
+
|
149
|
+
.overlay {
|
150
|
+
display: none;
|
151
|
+
background: rgba(0, 0, 0, 0.6);
|
152
|
+
position: absolute;
|
153
|
+
width: 100%;
|
154
|
+
height: 100%;
|
155
|
+
top: 0;
|
156
|
+
left: 0;
|
157
|
+
z-index: 1000;
|
158
|
+
}
|
159
|
+
|
160
|
+
.dialog-widget {
|
161
|
+
background: #FAFAFA;
|
162
|
+
border: 5px solid #DDD;
|
163
|
+
position: absolute;
|
164
|
+
left: 30%;
|
165
|
+
top: 100px;
|
166
|
+
padding: 20px;
|
167
|
+
width: 40%;
|
168
|
+
text-align: left;
|
169
|
+
}
|
170
|
+
|
171
|
+
.dialog-widget pre {
|
172
|
+
font-size: 1.2em;
|
173
|
+
}
|
174
|
+
|
175
|
+
.dialog-content {
|
176
|
+
position: relative;
|
177
|
+
}
|
178
|
+
|
179
|
+
p.label {
|
180
|
+
font-weight: bold;
|
181
|
+
margin: 0.3em;
|
182
|
+
}
|
183
|
+
|
184
|
+
.close_icon {
|
185
|
+
position: absolute;
|
186
|
+
right: -10px;
|
187
|
+
top: -10px;
|
188
|
+
}
|
189
|
+
|
190
|
+
form.autoclassify {
|
191
|
+
display: inline;
|
192
|
+
}
|
193
|
+
|
194
|
+
span.label_info {
|
195
|
+
display: inline-block;
|
196
|
+
width: 130px;
|
197
|
+
}
|