5467167b2fd690c393fe968dd6555386af2bf2df
[lazar] / lib / download.rb
1 module OpenTox
2
3   class Download
4
5     DATA = File.join(File.dirname(__FILE__),"..","data")
6
7     # Download classification dataset from PubChem into the data folder
8     # @param [Integer] aid PubChem Assay ID
9     # @param [String] active Name for the "Active" class
10     # @param [String] inactive Name for the "Inactive" class
11     # @param [String] species Species name
12     # @param [String] endpoint Endpoint name
13     # @param [Hash] qmrf Name and group for QMRF reports (optional)
14     def self.pubchem_classification aid: , active: , inactive: , species: , endpoint:, qmrf: nil
15       aid_url = File.join PUBCHEM_URI, "assay/aid/#{aid}"
16       
17       # Get assay data in chunks
18       # Assay record retrieval is limited to 10000 SIDs
19       # https://pubchemdocs.ncbi.nlm.nih.gov/pug-rest-tutorial$_Toc458584435
20       list = JSON.parse(RestClientWrapper.get(File.join aid_url, "sids/JSON?list_return=listkey").to_s)["IdentifierList"]
21       listkey = list["ListKey"]
22       size = list["Size"]
23       start = 0
24       csv = []
25       while start < size
26         url = File.join aid_url, "CSV?sid=listkey&listkey=#{listkey}&listkey_start=#{start}&listkey_count=10000"
27         csv += CSV.parse(RestClientWrapper.get(url).to_s).select{|r| r[0].match /^\d/} # discard header rows
28         start += 10000
29       end
30       warnings = []
31       name = endpoint.gsub(" ","_")+"-"+species.gsub(" ","_")
32       $logger.debug name
33       table = [["SID","SMILES",name]]
34       csv.each_slice(100) do |slice| # get SMILES in chunks, size limit is 100
35         cids = slice.collect{|s| s[2]}
36         pubchem_cids = []
37         JSON.parse(RestClientWrapper.get(File.join(PUBCHEM_URI,"compound/cid/#{cids.join(",")}/property/CanonicalSMILES/JSON")).to_s)["PropertyTable"]["Properties"].each do |prop|
38           i = cids.index(prop["CID"].to_s)
39           value = slice[i][3]
40           if value == "Active"
41             table << [slice[i][1].to_s,prop["CanonicalSMILES"],active]
42             pubchem_cids << prop["CID"].to_s
43           elsif value == "Inactive"
44             table << [slice[i][1].to_s,prop["CanonicalSMILES"],inactive]
45             pubchem_cids << prop["CID"].to_s
46           else
47             warnings << "Ignoring CID #{prop["CID"]}/ SMILES #{prop["CanonicalSMILES"]}, because PubChem activity is '#{value}'."
48           end
49         end
50         (cids-pubchem_cids).each { |cid| warnings << "Could not retrieve SMILES for CID '#{cid}', all entries are ignored." }
51       end
52       File.open(File.join(DATA,name+".csv"),"w+"){|f| f.puts table.collect{|row| row.join(",")}.join("\n")}
53       meta = {
54         :species => species,
55         :endpoint => endpoint,
56         :source => "https://pubchem.ncbi.nlm.nih.gov/bioassay/#{aid}",
57         :qmrf => qmrf,
58         :warnings => warnings
59       }
60       File.open(File.join(DATA,name+".json"),"w+"){|f| f.puts meta.to_json}
61       File.join(DATA,name+".csv")
62     end
63
64     # Download regression dataset from PubChem into the data folder
65     # Uses -log10 transformed experimental data in mmol units
66     # @param [String] aid PubChem Assay ID
67     # @param [String] species Species name
68     # @param [String] endpoint Endpoint name
69     # @param [Hash] qmrf Name and group for QMRF reports (optional)
70     def self.pubchem_regression aid: , species: , endpoint:, qmrf: nil
71       aid_url = File.join PUBCHEM_URI, "assay/aid/#{aid}"
72       
73       # Get assay data in chunks
74       # Assay record retrieval is limited to 10000 SIDs
75       # https://pubchemdocs.ncbi.nlm.nih.gov/pug-rest-tutorial$_Toc458584435
76       list = JSON.parse(RestClientWrapper.get(File.join aid_url, "sids/JSON?list_return=listkey").to_s)["IdentifierList"]
77       listkey = list["ListKey"]
78       size = list["Size"]
79       start = 0
80       csv = []
81       unit = nil
82       while start < size
83         url = File.join aid_url, "CSV?sid=listkey&listkey=#{listkey}&listkey_start=#{start}&listkey_count=10000"
84         # get unit
85         unit ||= CSV.parse(RestClientWrapper.get(url).to_s).select{|r| r[0] == "RESULT_UNIT"}[0][8]
86         csv += CSV.parse(RestClientWrapper.get(url).to_s).select{|r| r[0].match /^\d/} # discard header rows
87         start += 10000
88       end
89       warnings = []
90       name = endpoint.gsub(" ","_")+"-"+species.gsub(" ","_")
91       $logger.debug name
92       table = [["SID","SMILES","-log10(#{name} [#{unit}])"]]
93       csv.each_slice(100) do |slice| # get SMILES in chunks, size limit is 100
94         cids = slice.collect{|s| s[2]}
95         pubchem_cids = []
96         JSON.parse(RestClientWrapper.get(File.join(PUBCHEM_URI,"compound/cid/#{cids.join(",")}/property/CanonicalSMILES/JSON")).to_s)["PropertyTable"]["Properties"].each do |prop|
97           i = cids.index(prop["CID"].to_s)
98           value = slice[i][8]
99           if value
100             value = -Math.log10(value.to_f)
101             table << [slice[i][1].to_s,prop["CanonicalSMILES"],value]
102             pubchem_cids << prop["CID"].to_s
103           else
104             warnings << "Ignoring CID #{prop["CID"]}/ SMILES #{prop["CanonicalSMILES"]}, because PubChem activity is '#{value}'."
105           end
106         end
107         (cids-pubchem_cids).each { |cid| warnings << "Could not retrieve SMILES for CID '#{cid}', all entries are ignored." }
108       end
109       File.open(File.join(DATA,name+".csv"),"w+"){|f| f.puts table.collect{|row| row.join(",")}.join("\n")}
110       meta = {
111         :species => species,
112         :endpoint => endpoint,
113         :source => "https://pubchem.ncbi.nlm.nih.gov/bioassay/#{aid}",
114         :unit => unit,
115         :qmrf => qmrf,
116         :warnings => warnings
117       }
118       File.open(File.join(DATA,name+".json"),"w+"){|f| f.puts meta.to_json}
119       File.join(DATA,name+".csv")
120     end
121
122     # Combine mutagenicity data from Kazius, Hansen and EFSA and download into the data folder
123     def self.mutagenicity
124       $logger.debug "Mutagenicity"
125       # TODO add download/conversion programs to lazar dependencies
126       hansen_url = "http://doc.ml.tu-berlin.de/toxbenchmark/Mutagenicity_N6512.csv"
127       kazius_url = "http://cheminformatics.org/datasets/bursi/cas_4337.zip"
128       efsa_url = "https://data.europa.eu/euodp/data/storage/f/2017-07-19T142131/GENOTOX data and dictionary.xls"
129       
130       parts = File.join(DATA, "parts")
131       FileUtils.mkdir_p parts
132       Dir[File.join(parts,"hansen.*")].each{|f| FileUtils.rm f }
133       Dir[File.join(parts,"cas_4337.*")].each{|f| FileUtils.rm f }
134       Dir[File.join(parts,"efsa.*")].each{|f| FileUtils.rm f }
135       File.open(File.join(parts,"hansen-original.csv"),"w+"){|f| f.puts RestClientWrapper.get(hansen_url).to_s }
136
137       # convert hansen
138       hansen = CSV.read File.join(parts,"hansen-original.csv")
139       hansen.shift
140
141       map = {"0" => "non-mutagenic","1" => "mutagenic"}
142       File.open(File.join(parts,"hansen.csv"),"w+") do |f|
143         f.puts "ID,SMILES,Mutagenicity"
144         hansen.each do |row|
145           f.puts [row[0],row[5],map[row[2]]].join "," 
146         end
147       end
148       File.open(File.join(parts,"cas_4337.zip"),"w+"){|f| f.puts RestClientWrapper.get(kazius_url).to_s }
149       `cd #{parts} && unzip cas_4337.zip`
150       `cd #{parts} && wget #{URI.escape efsa_url} -O efsa.xls`
151       `cd #{parts} && xls2csv -s cp1252 -d utf-8 -x -c "        " efsa.xls > efsa.tsv`
152
153       # convert EFSA data to mutagenicity classifications
154       i = 0
155       db = {}
156       CSV.foreach(File.join(parts,"efsa.tsv"), :encoding => "UTF-8", :col_sep => "\t", :liberal_parsing => true) do |row|
157         if i > 0 and row[11] and !row[11].empty? and row[24].match(/Salmonella/i) and ( row[25].match("TA 98") or row[25].match("TA 100") ) and row[33]
158           begin
159             c = OpenTox::Compound.from_smiles(row[11].gsub('"','')).smiles
160           rescue
161             c = OpenTox::Compound.from_inchi(row[12]).smiles # some smiles (row[11]) contain non-parseable characters
162           end
163           db[c] ||= {}
164           db[c][:id] ||= row[2]
165           if row[33].match(/Positiv/i)
166             db[c][:value] = "mutagenic" # at least one positive result in TA 98 or TA 100
167           elsif row[33].match(/Negativ/i)
168             db[c][:value] ||= "non-mutagenic"
169           end
170         end
171         i += 1
172       end
173       File.open(File.join(parts,"efsa.csv"),"w+") do |f|
174         f.puts "ID,SMILES,Mutagenicity"
175         db.each do |s,v|
176           f.puts [v[:id],s,v[:value]].join ","
177         end
178       end
179
180       # merge datasets
181       hansen = Dataset.from_csv_file File.join(parts,"hansen.csv")
182       efsa = Dataset.from_csv_file File.join(parts,"efsa.csv")
183       kazius = Dataset.from_sdf_file File.join(parts,"cas_4337.sdf")
184       datasets = [hansen,efsa,kazius]
185       map = {"mutagen" => "mutagenic", "nonmutagen" => "non-mutagenic"}
186       dataset = Dataset.merge datasets: datasets, features: datasets.collect{|d| d.bioactivity_features.first}, value_maps: [nil,nil,map], keep_original_features: false, remove_duplicates: true
187       dataset.merged_features.first.name = "Mutagenicity"
188       File.open(File.join(DATA,"Mutagenicity-Salmonella_typhimurium.csv"),"w+"){|f| f.puts dataset.to_csv}
189       meta = {
190         :species => "Salmonella typhimurium",
191         :endpoint => "Mutagenicity",
192         :source => [kazius_url,hansen_url,efsa_url].join(", "),
193         :qmrf => { "group": "QMRF 4.10. Mutagenicity", "name": "OECD 471 Bacterial Reverse Mutation Test"},
194       }
195       File.open(File.join(DATA,"Mutagenicity-Salmonella_typhimurium.json"),"w+"){|f| f.puts meta.to_json}
196       
197       # cleanup
198       datasets << dataset
199       datasets.each{|d| d.delete }
200       File.join(DATA,"Mutagenicity-Salmonella_typhimurium.csv")
201     end
202
203     # Download Blood Brain Barrier Penetration dataset into the data folder
204     def self.blood_brain_barrier
205       url =  "http://cheminformatics.org/datasets/li/bbp2.smi"
206       name = "Blood_Brain_Barrier_Penetration-Human"
207       $logger.debug name
208       map = {"n" => "non-penetrating", "p" => "penetrating"}
209       table = CSV.parse RestClientWrapper.get(url).to_s, :col_sep => "\t"
210       File.open(File.join(DATA,name+".csv"),"w+") do |f|
211         f.puts "ID,SMILES,#{name}"
212         table.each do |row|
213           f.puts [row[1],row[0],map[row[3]]].join(",")
214         end
215       end
216       meta = {
217         :species => "Human",
218         :endpoint => "Blood Brain Barrier Penetration",
219         :source => url,
220         :qmrf => {"name": "QMRF 5.4. Toxicokinetics.Blood-brain barrier penetration", "group": "QMRF 5. Toxicokinetics"},
221       }
222       File.open(File.join(DATA,name+".json"),"w+"){|f| f.puts meta.to_json}
223     end
224
225     # Download the combined LOAEL dataset from Helma et al 2018 into the data folder
226     def self.loael
227       # TODO: fix url??
228       url = "https://raw.githubusercontent.com/opentox/loael-paper/revision/data/training_log10.csv"
229       name = "Lowest_observed_adverse_effect_level-Rats"
230       $logger.debug name
231       File.open(File.join(DATA,name+".csv"),"w+") do |f|
232         CSV.parse(RestClientWrapper.get(url).to_s) do |row|
233           f.puts [row[0],row[1]].join ","
234         end
235       end
236       meta = {
237         :species => "Rat",
238         :endpoint => "Lowest observed adverse effect level",
239         :source => url,
240         :unit => "mmol/kg_bw/day",
241         :qmrf => {
242           "name": "QMRF 4.14. Repeated dose toxicity",
243           "group": "QMRF 4.Human Health Effects"
244         }
245       }
246       File.open(File.join(DATA,name+".json"),"w+"){|f| f.puts meta.to_json}
247     end
248
249     # Download Daphnia dataset from http://www.michem.unimib.it/download/data/acute-aquatic-toxicity-to-daphnia-magna/ into the public folder
250     # The original file requires an email request, this is a temporary workaround
251     def self.daphnia
252       url = "https://raw.githubusercontent.com/opentox/lazar-public-data/master/regression/daphnia_magna_mmol_log10.csv"
253       name = "Acute_toxicity-Daphnia_magna"
254       $logger.debug name
255       File.open(File.join(DATA,name+".csv"),"w+") do |f|
256         f.puts RestClientWrapper.get(url).to_s
257       end
258       meta = { "species": "Daphnia magna",
259         "endpoint": "Acute toxicity",
260         "source": "http://www.michem.unimib.it/download/data/acute-aquatic-toxicity-to-daphnia-magna/",
261         "unit": "mmol/L",
262         "qmrf": {
263           "group": "QMRF 3.1. Short-term toxicity to Daphnia (immobilisation)",
264           "name": "EC C. 2. Daphnia sp Acute Immobilisation Test"
265         }
266       }
267       File.open(File.join(DATA,name+".json"),"w+"){|f| f.puts meta.to_json}
268     end
269
270     # Download all public lazar datasets into the data folder
271     def self.public_data
272
273       # Classification
274       [
275         {
276           :aid => 1205,
277           :species => "Rodents",
278           :endpoint => "Carcinogenicity",
279           :qmrf => {:group => "QMRF 4.12. Carcinogenicity", :name => "OECD 451 Carcinogenicity Studies"}
280         },{
281           :aid => 1208,
282           :species => "Rat",
283           :endpoint => "Carcinogenicity",
284           :qmrf => {:group => "QMRF 4.12. Carcinogenicity", :name => "OECD 451 Carcinogenicity Studies"}
285         },{
286           :aid => 1199,
287           :species => "Mouse",
288           :endpoint => "Carcinogenicity",
289           :qmrf => {:group => "QMRF 4.12. Carcinogenicity", :name => "OECD 451 Carcinogenicity Studies"}
290         }
291       ].each do |assay|
292         Download.pubchem_classification aid: assay[:aid], species: assay[:species], endpoint: assay[:endpoint], active: "carcinogen", inactive: "non-carcinogen", qmrf: assay[:qmrf]
293       end
294       Download.mutagenicity
295       Download.blood_brain_barrier
296
297       # Regression
298       [
299         {
300           :aid => 1195,
301           :species => "Human",
302           :endpoint => "Maximum Recommended Daily Dose",
303           :qmrf => {
304             "group": "QMRF 4.14. Repeated dose toxicity",
305             "name": "OECD 452 Chronic Toxicity Studies"
306           },
307         },{
308           :aid => 1208,
309           :species => "Rat (TD50)",
310           :endpoint => "Carcinogenicity",
311           :qmrf => {
312             :group => "QMRF 4.12. Carcinogenicity",
313             :name => "OECD 451 Carcinogenicity Studies"
314           }
315         },{
316           :aid => 1199,
317           :species => "Mouse (TD50)",
318           :endpoint => "Carcinogenicity",
319           :qmrf => {
320             :group => "QMRF 4.12. Carcinogenicity",
321             :name => "OECD 451 Carcinogenicity Studies"
322           }
323         },{
324           :aid => 1188,
325           :species => "Fathead minnow",
326           :endpoint => "Acute toxicity",
327           :qmrf => {
328             "group": "QMRF 3.3. Acute toxicity to fish (lethality)",
329             "name": "EC C. 1. Acute Toxicity for Fish"
330           }
331         }
332       ].each do |assay|
333         Download.pubchem_regression aid: assay[:aid], species: assay[:species], endpoint: assay[:endpoint], qmrf: assay[:qmrf]
334       end
335       
336       Download.loael
337       Download.daphnia
338
339 =begin
340         # 1204  estrogen receptor
341         # 1259408, # GENE-TOX
342         # 1159563 HepG2 cytotoxicity assay
343         # 588209 hepatotoxicity
344         # 1259333 cytotoxicity
345         # 1159569 HepG2 cytotoxicity counterscreen Measured in Cell-Based System Using Plate Reader - 2153-03_Inhibitor_Dose_DryPowder_Activity
346         # 2122 HTS Counterscreen for Detection of Compound Cytotoxicity in MIN6 Cells
347         # 116724 Acute toxicity determined after intravenal administration in mice
348         # 1148549 Toxicity in po dosed mouse assessed as mortality after 7 days
349 =end
350     end
351
352   end
353 end