Skip to content

Commit

Permalink
Merge pull request #2115 from ELIXIR-Belgium/issue-2106-support-non-t…
Browse files Browse the repository at this point in the history
…ext-based-attributes-in-dynamic-table

Issue 2106 support non text based attributes in dynamic table
  • Loading branch information
kdp-cloud authored Jan 29, 2025
2 parents 64bdf88 + e6ebdce commit 265940c
Show file tree
Hide file tree
Showing 10 changed files with 394 additions and 87 deletions.
73 changes: 67 additions & 6 deletions app/assets/javascripts/single_page/dynamic_table.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ const objectInputTemp = '<input type="hidden" name="_NAME_[]" id="inpt-_NAME_" v

const typeaheadSamplesUrl = "<%= typeahead_samples_path(linked_sample_type_id: '_LINKED_') %>";
const typeaheadCVUrl = "<%= typeahead_sample_controlled_vocabs_path(scv_id: '_CVID_') %>";
const typeaheadSopsUrl = "<%= dynamic_table_typeahead_sops_path(study_id: '__STUDY_ID__', assay_id: '__ASSAY_ID__') %>";
const typeaheadDataFilesUrl = "<%= typeahead_data_files_path %>";
const typeaheadStrainsUrl = "<%= typeahead_strains_path %>";

const handleSelect = (e) => {
$j(e).parents("table").DataTable().row(e.closest("tr")).data()[0] = e.is(":checked")
Expand All @@ -72,27 +75,53 @@ const handleSelect = (e) => {
};
$j.dynamicTable.prototype = {
init: function(rows, columns, options = {}) {
columns.forEach((c) => {
const studyId = options.studyId;
const assayId = options.assayId;

columns.forEach((c) => {
let linkedSamplesUrl;
let cvUrl;
if (c.linked_sample_type) {
let registeredSopUrl;

let isRegisteredSample = false;
let isCVList = false;
let isRegisteredSop = false;
let isRegisteredDataFile = false;
let isRegisteredStrain = false;
if (c.attribute_type) {
isRegisteredSample = c.attribute_type.base_type.includes("SeekSample");
isCVList = c.attribute_type.base_type === "CVList";
isRegisteredSop = c.attribute_type.base_type === "SeekSop";
isRegisteredDataFile = c.attribute_type.base_type === "SeekDataFile";
isRegisteredStrain = c.attribute_type.base_type === "SeekStrain";
}

if (isRegisteredSample) {
linkedSamplesUrl = typeaheadSamplesUrl.replace("_LINKED_", c.linked_sample_type);
const linkedSamples = retrieveLinkedSamples(linkedSamplesUrl);
c.linkedSampleIds = linkedSamples.map((ls) => ls.id);
}

if(c.is_cv_list && 'cv_id' in c){
if(isCVList && 'cv_id' in c){
cvUrl = typeaheadCVUrl.replace("_CVID_", c.cv_id);
}

if (isRegisteredSop) {
registeredSopUrl = typeaheadSopsUrl.replace('__STUDY_ID__', options.studyId).replace('__ASSAY_ID__', options.assayId);
}

c["render"] = function(data_, type, full, meta) {
let sanitizedData = sanitizeData(data_);
let data;
if(c.linked_sample_type){
if(isRegisteredSample){
data = sanitizedData && Array.isArray(sanitizedData) ? sanitizedData : [sanitizedData];
data = data[0]?.id ? data : [];
return registeredSamplesObjectsInput(c, data, options, linkedSamplesUrl);
} else if(c.is_cv_list && sanitizedData !== "#HIDDEN"){
} else if (isRegisteredSop) {
data = sanitizedData && Array.isArray(sanitizedData) ? sanitizedData : [sanitizedData];
data = data[0]?.id ? data : [];
return simpleObjectsInput(c, data, options, registeredSopUrl);
} else if(isCVList && sanitizedData !== "#HIDDEN"){
data = sanitizedData && Array.isArray(sanitizedData) ? sanitizedData : [sanitizedData];
data = data.map((e) => {
if (e?.id){
Expand All @@ -102,6 +131,14 @@ const handleSelect = (e) => {
}
});
return cvListObjectsInput(c, data, options, cvUrl);
} else if (isRegisteredDataFile) {
data = sanitizedData && Array.isArray(sanitizedData) ? sanitizedData : [sanitizedData];
data = data[0]?.id ? data : [];
return simpleObjectsInput(c, data, options, typeaheadDataFilesUrl);
} else if (isRegisteredStrain) {
data = sanitizedData && Array.isArray(sanitizedData) ? sanitizedData : [sanitizedData];
data = data[0]?.id ? data : [];
return simpleObjectsInput(c, data, options, typeaheadStrainsUrl);
} else if (sanitizedData === "#HIDDEN") {
return "<em><small>Hidden</small></em>";
} else {
Expand Down Expand Up @@ -630,10 +667,34 @@ function cvListObjectsInput(column, data, options, url){
.replace('_EXTRACLASS_', extraClass)
.replace('_TITLE_', titleText)
.replace('_LIMIT?_', '')
.replace('_ALLOW_FREE_TEXT_', allowNewItems);
.replace('_ALLOW_FREE_TEXT_', allowNewItems.toString());
}
}

function simpleObjectsInput(column, data, options, url) {
const existingOptions = data.map((e) => `<option selected="selected" title="ID: ${e.id}" value="${e.id}">${sanitizeData(e.title)}</option>`);
if (options.readonly) {
return data.map((e) => `<span title="ID: ${sanitizeData(e)}" class="badge">${sanitizeData(e)}</span>`).join(" ");
} else {
const typeaheadTemplate = 'typeahead/single_pages_samples';
const objectInputName = data.map((e) => sanitizeData(e)).join('-') + '-' + crypto.randomUUID();
const extraClass = '';
const titleText = '';
const allowNewItems = false;
setTimeout(ObjectsInput.init);

return objectInputTemp
.replace(/_NAME_/g, objectInputName)
.replace('_TYPEHEAD_', typeaheadTemplate)
.replace('_URL_', url)
.replace('_OPTIONS_', existingOptions)
.replace('_EXTRACLASS_', extraClass)
.replace('_TITLE_', titleText)
.replace('_LIMIT?_', '1')
.replace('_ALLOW_FREE_TEXT_', allowNewItems.toString());
}
}

const handleFailure = (table, res) => {
const errors = new Set();
errors.add("The operation can not be performed for one or some samples. The red cells indicate unacceptable values.");
Expand Down
20 changes: 20 additions & 0 deletions app/controllers/sops_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ def update
end
end

def dynamic_table_typeahead
return if params[:study_id].blank? && params[:assay_id].blank?

query = params[:query] || ''
asset = if params[:study_id].present?
Study.authorized_for('view').detect { |study| study.id.to_s == params[:study_id] }
else
Assay.authorized_for('view').detect { |assay| assay.id.to_s == params[:assay_id] }
end

sops = asset&.sops || []
filtered_sops = sops.select { |sop| sop.title&.downcase&.include?(query.downcase) }
items = filtered_sops.collect { |sop| { id: sop.id, text: sop.title } }

respond_to do |format|
format.json { render json: { results: items }.to_json }
end
end


private

def sop_params
Expand Down
20 changes: 11 additions & 9 deletions app/helpers/dynamic_table_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,17 @@ def transform_registered_sample_single(json_metadata, input_key)
def dt_cols(sample_type)
attribs = sample_type.sample_attributes.map do |a|
attribute = { title: a.title, name: sample_type.id.to_s, required: a.required, description: a.description,
is_title: a.is_title }
attribute.merge!({ cv_id: a.sample_controlled_vocab_id }) unless a.sample_controlled_vocab_id.blank?
is_seek_sample = a.sample_attribute_type.seek_sample?
is_seek_multi_sample = a.sample_attribute_type.seek_sample_multi?
is_cv_list = a.sample_attribute_type.seek_cv_list?
cv_allows_free_text = a.allow_cv_free_text
attribute.merge!({ multi_link: true, linked_sample_type: a.linked_sample_type_id }) if is_seek_multi_sample
attribute.merge!({ multi_link: false, linked_sample_type: a.linked_sample_type_id }) if is_seek_sample
attribute.merge!({ is_cv_list: true, cv_allows_free_text:}) if is_cv_list
is_title: a.is_title, attribute_type: a.sample_attribute_type }

if a.sample_attribute_type&.controlled_vocab?
cv_allows_free_text = a.allow_cv_free_text
attribute.merge!({ cv_allows_free_text: cv_allows_free_text, cv_id: a.sample_controlled_vocab_id })
end

if a.sample_attribute_type&.seek_sample_multi? || a.sample_attribute_type&.seek_sample?
attribute.merge!({ multi_link: a.sample_attribute_type&.seek_sample_multi?, linked_sample_type: a.linked_sample_type_id })
end

attribute
end
(dt_default_cols(sample_type.id.to_s) + attribs).flatten
Expand Down
1 change: 1 addition & 0 deletions app/views/isa_studies/_study_samples.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
window.sampleDynamicTable = new $j.dynamicTable('#study-samples-table')
const elem = $j("#btn_save_sample")
const options = {
studyId: <%= study&.id %>,
ajax:{
url: dynamicTableDataPath,
data: function(d) {
Expand Down
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,9 @@
end

resources :sops, concerns: [:has_content_blobs, :publishable, :has_doi, :has_versions, :asset, :explorable_spreadsheet] do
collection do
get :dynamic_table_typeahead
end
resources :people, :programmes, :projects, :investigations, :assays, :samples, :studies, :publications, :events, :workflows, :collections, only: [:index]
end

Expand Down
Loading

0 comments on commit 265940c

Please sign in to comment.