Skip to content

Commit

Permalink
feat: Enhance import samples for sdf
Browse files Browse the repository at this point in the history
* refactor export of sample solvent to export solvent name(not the solvent hash)- refactor export samples for sdf for melting and boiling points columns and write import sample job for sdf format and refactor and write  import samples jobs spec tests for sdf format

* fix typo & refactor boiling and melting points params in sample_api_spec test

* add density column to import samples with sdf format and refactor sample_api_spec file

* improve extract solvent and other functions for sdf export

Ref: #1364
  • Loading branch information
adambasha0 authored Sep 20, 2023
1 parent fc4d286 commit dc4283b
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 213 deletions.
92 changes: 46 additions & 46 deletions app/api/chemotion/sample_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,36 +74,19 @@ class SampleAPI < Grape::API
# Create a temp file in the tmp folder and sdf delayed job, and pass it to sdf delayed job
extname = File.extname(params[:file][:filename])
if extname.match(/\.(sdf?|mol)/i)
sdf_import = Import::ImportSdf.new(file_path: params[:file][:tempfile].path,
sdf_import = Import::ImportSdf.new(
file_path: params[:file][:tempfile].path,
collection_id: params[:currentCollectionId],
mapped_keys: {
description: {field: "description", displayName: "Description", multiple: true},
location: {field: "location", displayName: "Location"},
name: {field: "name", displayName: "Name"},
external_label: {field: "external_label", displayName: "External label"},
purity: {field: "purity", displayName: "Purity"},

molecule_name: { field: 'molecule_name', displayName: 'Molecule Name' },
short_label: { field: 'short_label', displayName: 'Short Label' },
real_amount: { field: 'real_amount', displayName: 'Real Amount' },
real_amount_unit: { field: 'real_amount_unit', displayName: 'Real Amount Unit' },
target_amount: { field: 'target_amount', displayName: 'Target Amount' },
target_amount_unit: { field: 'target_amount_unit', displayName: 'Target Amount Unit' },
molarity: { field: 'molarity', displayName: 'Molarity' },
density: { field: 'density', displayName: 'Density' },
melting_point: { field: 'melting_point', displayName: 'Melting Point' },
boiling_point: { field: 'boiling_point', displayName: 'Boiling Point' },
cas: { field: 'cas', displayName: 'Cas' },
},
current_user_id: current_user.id)
current_user_id: current_user.id,
)
sdf_import.find_or_create_mol_by_batch
return {
sdf: true, message: sdf_import.message,
data: sdf_import.processed_mol, status: sdf_import.status,
custom_data_keys: sdf_import.custom_data_keys.keys,
mapped_keys: sdf_import.mapped_keys,
collection_id: sdf_import.collection_id
}
sdf: true, message: sdf_import.message,
data: sdf_import.processed_mol, status: sdf_import.status,
custom_data_keys: sdf_import.custom_data_keys.keys,
mapped_keys: sdf_import.mapped_keys,
collection_id: sdf_import.collection_id
}
end
# Creates the Samples from the XLS/CSV file. Empty Array if not successful
file_size = params[:file][:tempfile].size
Expand All @@ -127,21 +110,22 @@ class SampleAPI < Grape::API
tmp_file_path = File.join('tmp', temp_filename)
# Write the contents of the uploaded file to the temporary file
File.binwrite(tmp_file_path, file[:tempfile].read)
ImportSamplesJob.perform_later(
tmp_file_path,
params[:currentCollectionId],
current_user.id,
file['filename'],
)
parameters = {
collection_id: params[:currentCollectionId],
user_id: current_user.id,
file_name: file['filename'],
file_path: tmp_file_path,
}
ImportSamplesJob.perform_later(parameters)
{ status: 'in progress', message: 'Importing samples in background' }
end
end
end

namespace :confirm_import do
desc "Create Samples from an Array of inchikeys"
desc 'Create Samples from an Array of inchikeys'
params do
requires :rows, type: Array, desc: "Selected Molecule from the UI"
requires :rows, type: Array, desc: 'Selected Molecule from the UI'
requires :currentCollectionId, type: Integer
requires :mapped_keys, type: Hash
end
Expand All @@ -151,17 +135,33 @@ class SampleAPI < Grape::API
end

post do
sdf_import = Import::ImportSdf.new(
collection_id: params[:currentCollectionId],
current_user_id: current_user.id,
rows: params[:rows],
mapped_keys: params[:mapped_keys]
)

sdf_import.create_samples
return {
sdf: true, message: sdf_import.message, status: sdf_import.status, error_messages: sdf_import.error_messages
}
rows = params[:rows]
if rows.length < 25
sdf_import = Import::ImportSdf.new(
collection_id: params[:currentCollectionId],
current_user_id: current_user.id,
rows: rows,
mapped_keys: params[:mapped_keys],
)
sdf_import.create_samples
return {
sdf: true, message: sdf_import.message,
status: sdf_import.status,
error_messages: sdf_import.error_messages
}
else
parameters = {
collection_id: params[:currentCollectionId],
user_id: current_user.id,
file_name: 'dummy.sdf',
sdf_rows: rows,
mapped_keys: params[:mapped_keys],
}
ImportSamplesJob.perform_later(parameters)
return {
message: 'importing samples in background',
}
end
end
end

Expand Down
23 changes: 19 additions & 4 deletions app/jobs/import_samples_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,26 @@ class ImportSamplesJob < ApplicationJob
Delayed::Worker.logger.error e
end

def perform(file_path, collection_id, user_id, file_name)
@user_id = user_id
def perform(params)
@user_id = params[:user_id]
file_path = params[:file_path]
file_format = File.extname(params[:file_name])
begin
import = Import::ImportSamples.new(file_path, collection_id, user_id, file_name)
@result = import.process
case file_format
when '.xlsx'
import = Import::ImportSamples.new(file_path, params[:collection_id], @user_id, params[:file_name])
@result = import.process
when '.sdf'
sdf_import = Import::ImportSdf.new(
collection_id: params[:collection_id],
current_user_id: @user_id,
rows: params[:sdf_rows],
mapped_keys: params[:mapped_keys],
)
sdf_import.create_samples
@result = {}
@result[:message] = sdf_import.message
end
rescue StandardError => e
Delayed::Worker.logger.error e
ensure
Expand Down
23 changes: 15 additions & 8 deletions app/packs/src/fetchers/SamplesFetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,11 @@ export default class SamplesFetcher {
}

static importSamplesFromFileConfirm(params) {
let promise = fetch('/api/v1/samples/confirm_import/', {
const promise = fetch('/api/v1/samples/confirm_import/', {
credentials: 'same-origin',
method: 'post',
headers: {
'Accept': 'application/json',
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
Expand All @@ -182,16 +182,23 @@ export default class SamplesFetcher {
mapped_keys: params.mapped_keys,
})
}).then((response) => {
return response.json();
response.json();
}).then((json) => {
for (let i = 0; i < json.error_messages.length; i++) {
if (Array.isArray(json.error_messages)) {
json.error_messages.forEach((message) => {
NotificationActions.add({
message,
level: 'error',
autoDismiss: 10
});
});
} else {
NotificationActions.add({
message: json.error_messages[i],
level: 'error',
message: json.error_messages || json.message,
level: json.message ? 'success' : 'error',
autoDismiss: 10
});
};

}
return json;
}).catch((errorMessage) => {
console.log(errorMessage);
Expand Down
4 changes: 3 additions & 1 deletion lib/export/export_excel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def literatures_info(ids)
output = output.join("\n")
output
end

def filter_with_permission_and_detail_level(sample)
# return all data if sample in own collection
if sample['shared_sync'] == 'f' || sample['shared_sync'] == false
Expand All @@ -125,6 +125,8 @@ def filter_with_permission_and_detail_level(sample)
regex = /[\[\]()]/
string = sample[column].gsub(regex, '')
string.split(',').join(' - ')
elsif column == 'solvent'
extract_label_from_solvent_column(sample[column]) || ''
else
sample[column]
end
Expand Down
18 changes: 16 additions & 2 deletions lib/export/export_sdf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ def filter_with_permission_and_detail_level(sample)
end
data = data.rstrip
data += "\n"

@headers.each do |column|
column_data = format_field(column, sample[column])
data.concat(column_data)
Expand Down Expand Up @@ -73,9 +72,24 @@ def filter_with_permission_and_detail_level(sample)
data.concat("\$\$\$\$\n")
end

def extract_reference_values(raw_value)
regex = /[\[\]()]/
string = raw_value.gsub(regex, '')
string.split(',').join(' - ')
end

def format_field(column, raw_value)
field = column.gsub(/\s+/, '_').upcase
value = validate_value(raw_value)
reference_values = ['melting pt', 'boiling pt']
sample_column =
if reference_values.include?(column)
extract_reference_values(raw_value)
elsif column == 'solvent'
extract_label_from_solvent_column(raw_value) || ''
else
raw_value
end
value = validate_value(sample_column)
"> <#{field}>\n#{value}\n\n"
end

Expand Down
16 changes: 15 additions & 1 deletion lib/export/export_table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,28 @@ class ExportTable
'r ref', 'r eq'
].freeze


HEADERS_ANALYSIS_0 = [].freeze
HEADERS_ANALYSIS = ["name", "description", "uuid", "kind", "status", "content"].freeze
HEADERS_DATASET_0 = [].freeze
HEADERS_DATASET = ["dataset name", "instrument", "dataset description"].freeze
HEADERS_ATTACHMENT_0 = [].freeze
HEADERS_ATTACHMENT = ["filename", "checksum"].freeze

def extract_label_from_solvent_column(sample_column)
return unless sample_column.is_a?(String) && !sample_column.empty?

solvent_hash = begin
JSON.parse(sample_column)
rescue StandardError
nil
end

return nil if solvent_hash.nil?

solvent_values = solvent_hash.map { |solvent| solvent&.fetch('label', nil) }
solvent_values.compact.join('-')
end

def generate_headers(table, excluded_columns = [], selected_columns = [])
@row_headers = @samples.columns - excluded_columns
@headers = @row_headers - %w[s_id ts co_id scu_id shared_sync pl dl_s dl_wp dl_r m_image molfile_version]
Expand Down
21 changes: 15 additions & 6 deletions lib/import/import_samples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,26 @@ def included_fields
Sample.attribute_names - excluded_fields
end

def construct_solvents_array(solvents)
solvents_array = solvents.split('-')
solvents_array.map(&:capitalize)
end

def handle_sample_solvent_column(sample, row)
return unless row['solvent'].is_a? String

solvent = Chemotion::SampleConst.solvents_smiles_options.find { |s| s[:label].include?(row['solvent']) }
if solvent.present?
solvent_column = [{ label: solvent[:value][:external_label],
smiles: solvent[:value][:smiles],
ratio: '100' }]
solvent_array = construct_solvents_array(row['solvent'])
solvent_column = []
solvent_array.each do |element|
solvent = Chemotion::SampleConst.solvents_smiles_options.find { |s| s[:label].include?(element) }
next if solvent.blank?

solvent_column.push({ label: solvent[:value][:external_label],
smiles: solvent[:value][:smiles],
ratio: '1' })
end
sample['solvent'] = '' if sample['solvent'].is_a? String
sample['solvent'] = solvent_column if solvent.present?
sample['solvent'] = solvent_column unless solvent_column.empty?
end

# format row[field] for melting and boiling point
Expand Down
Loading

0 comments on commit dc4283b

Please sign in to comment.