1
|
|
|
require 'yaml' |
2
|
|
|
require 'fileutils' |
3
|
|
|
|
4
|
|
|
require 'geodiver/config' |
5
|
|
|
require 'geodiver/exceptions' |
6
|
|
|
require 'geodiver/load_geo_db' |
7
|
|
|
require 'geodiver/logger' |
8
|
|
|
require 'geodiver/geo_analysis' |
9
|
|
|
require 'geodiver/geo_analysis_helper' |
10
|
|
|
require 'geodiver/routes' |
11
|
|
|
require 'geodiver/server' |
12
|
|
|
require 'geodiver/version' |
13
|
|
|
|
14
|
|
|
# GeoDiver NameSpace |
15
|
|
|
module GeoDiver |
16
|
|
|
class << self |
17
|
|
|
def environment |
18
|
|
|
ENV['RACK_ENV'] |
19
|
|
|
end |
20
|
|
|
|
21
|
|
|
def verbose? |
22
|
|
|
@verbose ||= (environment == 'development') |
23
|
|
|
end |
24
|
|
|
|
25
|
|
|
def root |
26
|
|
|
File.dirname(File.dirname(__FILE__)) |
27
|
|
|
end |
28
|
|
|
|
29
|
|
|
def ssl? |
30
|
|
|
@config[:ssl] |
31
|
|
|
end |
32
|
|
|
|
33
|
|
|
def logger |
34
|
|
|
@logger ||= Logger.new(STDOUT) |
35
|
|
|
end |
36
|
|
|
|
37
|
|
|
# Setting up the environment before running the app... |
38
|
|
|
# We don't validate port and host settings. If GeoDiver is run |
39
|
|
|
# self-hosted, bind will fail on incorrect values. If GeoDiver |
40
|
|
|
# is run via Apache/Nginx + Passenger, we don't need to worry. |
41
|
|
|
def init(config = {}) |
42
|
|
|
@config = Config.new(config) |
43
|
|
|
Thread.abort_on_exception = true if verbose? |
44
|
|
|
|
45
|
|
|
init_dirs |
46
|
|
|
set_up_default_user_dir |
47
|
|
|
check_num_threads |
48
|
|
|
# generate_exemplar_results |
49
|
|
|
|
50
|
|
|
self |
51
|
|
|
end |
52
|
|
|
|
53
|
|
|
attr_reader :config, :temp_dir, :public_dir, :users_dir, :db_dir, |
54
|
|
|
:exemplar_results |
55
|
|
|
|
56
|
|
|
# Starting the app manually |
57
|
|
|
def run |
58
|
|
|
check_host |
59
|
|
|
Server.run(self) |
60
|
|
|
rescue Errno::EADDRINUSE |
61
|
|
|
puts "** Could not bind to port #{config[:port]}." |
62
|
|
|
puts " Is GeoDiver already accessible at #{server_url}?" |
63
|
|
|
puts ' No? Try running GeoDiver on another port, like so:' |
64
|
|
|
puts |
65
|
|
|
puts ' geodiver -p 4570.' |
66
|
|
|
rescue Errno::EACCES |
67
|
|
|
puts "** Need root privilege to bind to port #{config[:port]}." |
68
|
|
|
puts ' It is not advisable to run GeoDiver as root.' |
69
|
|
|
puts ' Please use Apache/Nginx to bind to a privileged port.' |
70
|
|
|
end |
71
|
|
|
|
72
|
|
|
def on_start |
73
|
|
|
puts '** GeoDiver is ready.' |
74
|
|
|
puts " Go to #{server_url} in your browser and start analysing GEO datasets!" |
75
|
|
|
puts ' Press CTRL+C to quit.' |
76
|
|
|
open_in_browser(server_url) |
77
|
|
|
end |
78
|
|
|
|
79
|
|
|
def on_stop |
80
|
|
|
puts |
81
|
|
|
puts '** Thank you for using GeoDiver :).' |
82
|
|
|
# Add Citation Notice Here |
83
|
|
|
end |
84
|
|
|
|
85
|
|
|
# Rack-interface. |
86
|
|
|
# |
87
|
|
|
# Inject our logger in the env and dispatch request to our controller. |
88
|
|
|
def call(env) |
89
|
|
|
env['rack.logger'] = logger |
90
|
|
|
Routes.call(env) |
91
|
|
|
end |
92
|
|
|
|
93
|
|
|
private |
94
|
|
|
|
95
|
|
|
# Set up the directory structure in @config[:gd_serve_dir] |
96
|
|
|
def init_dirs |
97
|
|
|
default_dirs |
98
|
|
|
init_public_dir |
99
|
|
|
FileUtils.mkdir_p @users_dir unless Dir.exist? @users_dir |
100
|
|
|
FileUtils.mkdir_p @db_dir unless Dir.exist? @db_dir |
101
|
|
|
end |
102
|
|
|
|
103
|
|
|
def default_dirs |
104
|
|
|
config[:gd_serve_dir] = File.expand_path config[:gd_serve_dir] |
105
|
|
|
@public_dir = File.join(config[:gd_serve_dir], 'public') |
106
|
|
|
@users_dir = File.join(config[:gd_serve_dir], 'Users') |
107
|
|
|
@db_dir = File.join(config[:gd_serve_dir], 'DBs') |
108
|
|
|
logger.debug "GeoDiver Directory: #{config[:gd_serve_dir]}" |
109
|
|
|
logger.debug "@public_dir Directory: #{@public_dir}" |
110
|
|
|
logger.debug "@users_dir Directory: #{@users_dir}" |
111
|
|
|
logger.debug "@db_dir Directory: #{@db_dir}" |
112
|
|
|
end |
113
|
|
|
|
114
|
|
|
# Public Directory structure |
115
|
|
|
def init_public_dir |
116
|
|
|
FileUtils.mkdir_p @public_dir unless Dir.exist?(@public_dir) |
117
|
|
|
root_assets = File.join(GeoDiver.root, 'public/assets') |
118
|
|
|
FileUtils.rm_rf(File.join(@public_dir, 'assets')) |
119
|
|
|
if environment == 'development' |
120
|
|
|
FileUtils.ln_s(root_assets, @public_dir) |
121
|
|
|
else |
122
|
|
|
FileUtils.cp_r(root_assets, @public_dir) |
123
|
|
|
end |
124
|
|
|
init_public_gd_dir(@public_dir) |
125
|
|
|
end |
126
|
|
|
|
127
|
|
|
def init_public_gd_dir(public_dir) |
128
|
|
|
root_gd = File.join(GeoDiver.root, 'public/GeoDiver') |
129
|
|
|
public_gd = File.join(public_dir, 'GeoDiver') |
130
|
|
|
return if File.exist?(public_gd) |
131
|
|
|
FileUtils.cp_r(root_gd, public_dir) |
132
|
|
|
end |
133
|
|
|
|
134
|
|
|
def set_up_default_user_dir |
135
|
|
|
user_dir = File.join(GeoDiver.users_dir, 'geodiver') |
136
|
|
|
user_public = File.join(GeoDiver.public_dir, 'GeoDiver/Users') |
137
|
|
|
FileUtils.mkdir(user_dir) unless Dir.exist?(user_dir) |
138
|
|
|
return if File.exist? File.join(user_public, 'geodiver') |
139
|
|
|
FileUtils.ln_s(user_dir, user_public) |
140
|
|
|
end |
141
|
|
|
|
142
|
|
|
def check_num_threads |
143
|
|
|
config[:num_threads] = Integer(config[:num_threads]) |
144
|
|
|
raise NUM_THREADS_INCORRECT unless config[:num_threads] > 0 |
145
|
|
|
|
146
|
|
|
logger.debug "Will use #{config[:num_threads]} threads to run GeoDiver." |
147
|
|
|
return unless config[:num_threads] > 256 |
148
|
|
|
logger.warn "Number of threads set at #{config[:num_threads]} is" \ |
149
|
|
|
' unusually high.' |
150
|
|
|
end |
151
|
|
|
|
152
|
|
|
# Check and warn user if host is 0.0.0.0 (default). |
153
|
|
|
def check_host |
154
|
|
|
return unless config[:host] == '0.0.0.0' |
155
|
|
|
logger.warn 'Will listen on all interfaces (0.0.0.0).' \ |
156
|
|
|
' Consider using 127.0.0.1 (--host option).' |
157
|
|
|
end |
158
|
|
|
|
159
|
|
|
def server_url |
160
|
|
|
host = config[:host] |
161
|
|
|
host = 'localhost' if ['127.0.0.1', '0.0.0.0'].include? host |
162
|
|
|
proxy = GeoDiver.ssl? ? 'https' : 'http' |
163
|
|
|
"#{proxy}://#{host}:#{config[:port]}" |
164
|
|
|
end |
165
|
|
|
|
166
|
|
|
def open_in_browser(server_url) |
167
|
|
|
return if using_ssh? || verbose? |
168
|
|
|
if RUBY_PLATFORM =~ /linux/ && xdg? |
169
|
|
|
system "xdg-open #{server_url}" |
170
|
|
|
elsif RUBY_PLATFORM =~ /darwin/ |
171
|
|
|
system "open #{server_url}" |
172
|
|
|
end |
173
|
|
|
end |
174
|
|
|
|
175
|
|
|
def using_ssh? |
176
|
|
|
true if ENV['SSH_CLIENT'] || ENV['SSH_TTY'] || ENV['SSH_CONNECTION'] |
177
|
|
|
end |
178
|
|
|
|
179
|
|
|
def xdg? |
180
|
|
|
true if ENV['DISPLAY'] && system('which xdg-open > /dev/null 2>&1') |
181
|
|
|
end |
182
|
|
|
|
183
|
|
|
# Test if the RCore is working and also produce the exemplar results |
184
|
|
|
def generate_exemplar_results |
185
|
|
|
logger.debug 'Testing RCore and producing Exemplar Results Page' |
186
|
|
|
session_geodb = load_geo_db |
187
|
|
|
email = 'geodiver' |
188
|
|
|
r = GeoAnalysis.run(default_params, email, server_url, session_geodb) |
189
|
|
|
assert_rcore_works(r) |
190
|
|
|
@exemplar_results = File.join('geodiver', r[:geo_db], r[:uniq_result_id]) |
191
|
|
|
logger.debug "Exemplar Results page is available at: #{r[:results_url]}" |
192
|
|
|
end |
193
|
|
|
|
194
|
|
|
def load_geo_db |
195
|
|
|
LoadGeoData.run('geo_db' => 'GDS724') |
196
|
|
|
LoadGeoData.convert_geodb_into_rdata('GDS724') |
197
|
|
|
end |
198
|
|
|
|
199
|
|
|
def assert_rcore_works(r) |
200
|
|
|
return if r[:overview_exit_code].zero? && r[:dgea_exit_code].zero? && |
201
|
|
|
r[:gage_exit_code].zero? |
202
|
|
|
raise RCORE_FAILURE |
203
|
|
|
end |
204
|
|
|
|
205
|
|
|
def default_params |
206
|
|
|
{ 'geo_db' => 'GDS724', 'user' => 'Z2VvZGl2ZXI=', 'factor' => 'tissue', |
207
|
|
|
'groupa' => ['peripheral blood lymphocyte'], 'groupb' => ['kidney'], |
208
|
|
|
'dgea' => 'on', 'dgea_toptable' => 'on', |
209
|
|
|
'dgea_number_top_genes' => '250', |
210
|
|
|
'dgea_volcano_pValue_cutoff' => 'fdr', 'dgea_heatmap' => 'on', |
211
|
|
|
'dgea_heatmap_rows' => '100', |
212
|
|
|
'dgea_heatmap_distance_method' => 'euclidean', |
213
|
|
|
'dgea_heatmap_clustering_method' => 'complete', |
214
|
|
|
'dgea_cluster_by_genes' => 'true', 'dgea_cluster_by_samples' => 'true', |
215
|
|
|
'dgea_cluster_based_on' => 'on', |
216
|
|
|
'dgea_volcano' => 'on', 'gsea' => 'on', 'gsea_type' => 'ExpVsCtrl', |
217
|
|
|
'gsea_control_group' => 'on', 'gsea_dataset' => 'KEGG', |
218
|
|
|
'gsea_heatmap' => 'on', 'gsea_heatmap_rows' => '100', |
219
|
|
|
'gsea_heatmap_distance_method' => 'euclidean', |
220
|
|
|
'gsea_heatmap_clustering_method' => 'complete', |
221
|
|
|
'gsea_cluster_by_genes' => 'true', 'gsea_cluster_by_samples' => 'true', |
222
|
|
|
'gsea_cluster_based_on' => 'on' } |
223
|
|
|
end |
224
|
|
|
end |
225
|
|
|
end |
226
|
|
|
|