Skip to content

Commit

Permalink
Merge branch 'main' into full-test-suite
Browse files Browse the repository at this point in the history
  • Loading branch information
stuzart committed Nov 9, 2023
2 parents 935d2d6 + 4d0808b commit 8fb0352
Show file tree
Hide file tree
Showing 189 changed files with 3,926 additions and 16,520 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ gem 'RedCloth', '>=4.3.0'
gem 'simple-spreadsheet-extractor', '~> 0.18.0'
gem 'open4'
gem 'sample-template-generator', '~>0.7'
gem 'rmagick', '4.2.5'
gem 'rmagick', '5.3.0'
gem 'rest-client', '~>2.0'
gem 'factory_bot', '~> 6.2.1'
gem 'bio', '~> 1.5.1'
Expand Down
6 changes: 4 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ GEM
rack
rake (>= 0.8.1)
pg (1.3.1)
pkg-config (1.5.5)
pothoven-attachment_fu (3.3.2)
pr_geohash (1.0.0)
private_address_check (0.5.0)
Expand Down Expand Up @@ -729,7 +730,8 @@ GEM
nokogiri
rexml (3.2.5)
rfc-822 (0.4.1)
rmagick (4.2.5)
rmagick (5.3.0)
pkg-config (~> 1.4)
ro-bundle (0.3.0)
addressable (~> 2.8.0)
json (~> 2.3.0)
Expand Down Expand Up @@ -1066,7 +1068,7 @@ DEPENDENCIES
responders
rest-client (~> 2.0)
rfc-822
rmagick (= 4.2.5)
rmagick (= 5.3.0)
ro-bundle (~> 0.3.0)
ro-crate (~> 0.5.1)
rspec-rails (~> 5.1)
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/controlled_vocabs.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ CVTerms = {
CVTerms.disableAddTermButton('<%= I18n.t('controlled_vocabs.add_new_term_disabled_reason') %>');
var link = $j('a#selected-ols-link');
link.text(selected.text);
link.attr('href', 'https://www.ebi.ac.uk/ols/ontologies/' + selected.value);
link.attr('href', '<%= Ebi::OlsClient::ROOT_URL %>/ontologies/' + selected.value);
}
});

Expand Down
2 changes: 0 additions & 2 deletions app/assets/javascripts/sample_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,10 @@ var SampleTypes = {
attributeTypeChanged: function (e, resetSelection=true) {
//check if it is a controlled vocab, and change the state of the controlled vocab selector if need be
var use_cv = $j(this).find(':selected').data('use-cv');
var is_ontology = $j(this).find(':selected').data('is-ontology');
var cv_element = $j(this).siblings('.controlled-vocab-block');
if (use_cv) {
var cv_selection = cv_element.find('.controlled-vocab-selection');
cv_selection.find('option').show();
cv_selection.find(`option[data-is-ontology="${!is_ontology}"]`).hide();
if (resetSelection) cv_selection.find('option:selected').prop("selected", false);
cv_element.show();
}
Expand Down
13 changes: 10 additions & 3 deletions app/assets/javascripts/single_page/dynamic_table.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@ const handleSelect = (e) => {
if (c.multi_link) {
data = data && Array.isArray(data) ? data : [data];
data = data[0]?.id ? data : [];
const existingOptions = data.map((e) => `<option selected="selected" title="ID: ${e.id}" value="${e.id}">${e.title}</option>`).join("");
const existingOptions = data.map((e) => {
isHiddenInput = (e.title == '#HIDDEN')
if (isHiddenInput) {
return `<option selected="selected" title="ID: hidden" value="hidden">hidden</option>`
} else {
return `<option selected="selected" title="ID: ${e.id}" value="${e.id}">${e.title}</option>`
}
}).join("");
if (options.readonly) {
return data.map((e) => `<span title="ID: ${e.id}" class="badge">${e.title}</span>`).join(" ");
} else {
Expand All @@ -61,15 +68,15 @@ const handleSelect = (e) => {
const linkedSamples = retrieveLinkedSamples(url);
const linkedSampleIds = linkedSamples.map((ls) => ls.id);
const unLinkedSamples = data.reduce(function(filtered, sample) {
if(!linkedSampleIds.includes(parseInt(sample.id))){
if(!linkedSampleIds.includes(parseInt(sample.id)) && sample.title != '#HIDDEN'){
filtered.push(sample);
}
return filtered;
}, []);
const hasUnlinkedSamples = unLinkedSamples.length > 0 ? true : false;

const extraClass = hasUnlinkedSamples ? 'select2__error' : '';
const titleText = hasUnlinkedSamples ? `Sample(s) '${unLinkedSamples.map(uls => uls.title).join(', ')}' not recognised as sources. Please correct this issue!` : '';
const titleText = hasUnlinkedSamples ? `Sample(s) '${unLinkedSamples.map(uls => uls.title).join(', ')}' not recognised as input. Please correct this issue!` : '';

return objectInputTemp
.replace(/_NAME_/g, objectInputName)
Expand Down
111 changes: 64 additions & 47 deletions app/assets/javascripts/single_page/index.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -207,61 +207,78 @@ async function batchDeleteSample(sampleTypes) {
// Posts the content of the dynamic table to the single_pages_controller,
// which generates an excel workbook based on a template.
async function exportToExcel(tableName, studyId, assayId, sampleTypeId) {
const headerRow = $j(`table[id=${tableName}] thead tr`)["0"];
const headerRow = $j(`table[id=${tableName}] thead tr`)["0"];

// Cells from the table body => Each row = array
// For some reason they contain null values, which should be filtered out
let bodyCells = $j(`table[id=${tableName}]`).DataTable().data().toArray().map(subarray => subarray.filter(function(el){return el != null}));
// Cells from the table body => Each row = array
// For some reason they contain null values, which should be filtered out
let bodyCells = $j(`table[id=${tableName}]`).DataTable().data().toArray().map(subarray => subarray.filter(function(el){return el != null}));

// Return array of table header cells from headerRow
let headerCells = [];
$j.each(headerRow.cells, function (i, v) {
headerCells.push(v.textContent);
});
// Checks whether the dynamic table has errors
// The excel export will be aborted as long as the dynamic table has errors
hasErrorcells = $j(`table[id=${tableName}]`).find('select.select2__error').size() > 0
if (hasErrorcells) {
alert('It appears this sample table has some errors. Please correct the errors in the current sample table and try downloading again.');
return;
}

// Construct the 'struct' of the table data
let data = [];
for (let i = 0; i < bodyCells.length; i++) {
const row = bodyCells[i];
// If a row has been checked / unchecked,
// the datatable body gets one extra hidden cell at index 1,
// compared to the table header. This has to be removed.
if (row.length !== headerCells.length)row.splice(1,1);
let obj = {};
for (let j = 0; j < headerCells.length; j++) {
const attr = headerCells[j];
const val = row[j];
// Return array of table header cells from headerRow
let headerCells = [];
$j.each(headerRow.cells, function (i, v) {
headerCells.push(v.textContent);
});

// The source inputs are wrapped in an object and must be unwrapped first
// And then packaged as a single element
if(j === 0) {
obj["selected"] = (val === "") ? false : val;
} else {
obj[attr] = val;
}
if(obj.selected) data.push(obj);
// Construct the 'struct' of the table data
let data = [];
for (let i = 0; i < bodyCells.length; i++) {
const row = bodyCells[i];
// If a row has been checked / unchecked,
// the datatable body gets one extra hidden cell at index 1,
// compared to the table header. This has to be removed.
if (row.length !== headerCells.length)row.splice(1,1);
let obj = {};
let hasHiddenInputs = false;
for (let j = 0; j < headerCells.length; j++) {
const attr = headerCells[j];
const val = row[j];

// Check whether sample has one or more hidden Inputs or if sample is completely hidden
if (attr.toLowerCase().includes('input')) {
if (Array.isArray(val)){
hasHiddenInputs = val.some(input => input.title === '#HIDDEN'); // Sample has hidden inputs
} else {
hasHiddenInputs = val === '#HIDDEN'; // Sample is completely hidden
}
}

// Use ajax for POST request with table data in the body
// If succes => Redirect to download path for retrieving excel file
$j.ajax({
type: 'POST',
url: '<%= export_to_excel_single_pages_path() %>',
data: { sample_data: JSON.stringify(data),
sample_type_id: JSON.stringify(sampleTypeId),
study_id: JSON.stringify(studyId),
assay_id: JSON.stringify(assayId),
},
success: function(response) {
downloadUrl = `<%= download_samples_excel_single_pages_path() %>?uuid=${response.uuid}`;
window.location.href = downloadUrl;
},
error: function(response) {
alert(`Failed to export through excel!\nStatus: ${response.status}\nError: ${JSON.stringify(response.error().statusText)}`);
}
});
// The source inputs are wrapped in an object and must be unwrapped first
// And then packaged as a single element
if(j === 0) {
obj["selected"] = (val === "") ? false : val;
} else {
obj[attr] = val;
}
}
if (!hasHiddenInputs) data.push(obj);
}

// Use ajax for POST request with table data in the body
// If succes => Redirect to download path for retrieving excel file
$j.ajax({
type: 'POST',
url: '<%= export_to_excel_single_pages_path() %>',
data: { sample_data: JSON.stringify(data),
sample_type_id: JSON.stringify(sampleTypeId),
study_id: JSON.stringify(studyId),
assay_id: JSON.stringify(assayId),
},
success: function(response) {
downloadUrl = `<%= download_samples_excel_single_pages_path() %>?uuid=${response.uuid}`;
window.location.href = downloadUrl;
},
error: function(response) {
alert(`Failed to export through excel!\nStatus: ${response.status}\nError: ${JSON.stringify(response.error().statusText)}`);
}
});
}

async function batchUpdateSample(sampleTypes) {
Expand Down
61 changes: 54 additions & 7 deletions app/assets/javascripts/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ Templates.init = function (elem) {
},
{ title: "PID", width: "10%" },
{ title: "pos" },
{ title: "isa_tag_id" },
{ title: "ISA Tag ID", width: "10%" },
{ title: "ISA Tag", width: "10%" },
{
title: "Remove",
width: "5%",
Expand Down Expand Up @@ -89,7 +90,7 @@ function loadTemplates(data) {

$j.each(Object.keys(categorized), (i, key) => {
const elem = $j(`<optgroup label=${key}></optgroup>`);

$j.each(categorized[key], (j, sub_item) => {
elem.append(
$j(`<option>${sub_item.title}</option>`).attr("value", sub_item.template_id).text(key.title)
Expand Down Expand Up @@ -119,13 +120,14 @@ Templates.mapData = (data) =>
item.is_title,
item.pid,
item.pos,
item.isa_tag_id
item.isa_tag_id,
item.isa_tag_title
]);

function loadFilterSelectors(data) {
$j.each($j("select[id^='templates_']"), (i, elem) => {
const key = elem.getAttribute("data-key");

// Gets the set of values to choose from per data-key
let dt = [...new Set(data.map((item) => item[key]))];
// If the key == level => options should be filtered out, depending on the 'field_name' context.
Expand All @@ -136,7 +138,7 @@ function loadFilterSelectors(data) {
} else if(Templates.context.field_name === 'source_sample_type'){
dt = dt.filter(lvl => lvl === "study source")
} else if(Templates.context.field_name === 'sample_type') {
dt = dt.filter(lvl => lvl === "assay")
dt = dt.filter(lvl => ["assay - material", "assay - data file"].includes(lvl))
}
$j(elem).find("option").remove(); // Removes all options, even the first, i.e. "not selected"
}
Expand All @@ -159,6 +161,42 @@ function loadFilterSelectors(data) {
});
}

function get_filtered_isa_tags(level) {
var result;
$j.ajax({
type: 'POST',
async: false,
url: '/templates/filter_isa_tags_by_level',
data: {level: level},
dataType: 'json',
success: function(res) {
result = res.result;
},
error: function (errMsg) {
alert(`Couldn't find valid ISA Tags because of the following error:\n${errMsg}`);
result = [];
}
});
return result;
}

function updateIsaTagSelect(template_level, attribute_row) {
const isa_tags = get_filtered_isa_tags(template_level);

// Remove all options first, except blank one
$j(attribute_row).find('select[data-attr="isa_tag_title"] option:not([value=""])').each(function() {
$j(this).remove();
});

// Append filtered option to a new attribute row
$j.each(isa_tags, function (i, tag) {
$j(attribute_row).find('select[data-attr="isa_tag_title"]').append($j('<option>', {
value: tag.value,
text: tag.text
}));
});
}

const applyTemplate = () => {
const id = $j("#source_select").find(":selected").val();
const data = templates.find((t) => t.template_id == id);
Expand All @@ -170,10 +208,17 @@ const applyTemplate = () => {
const attribute_table = "#attribute-table" + suffix;
const attribute_row = "#new-attribute-row" + suffix;
const addAttributeRow = "#add-attribute-row" + suffix;
updateIsaTagSelect(data.level, attribute_row);

$j(`${attribute_table} tbody`).find("tr:not(:last)").remove();
SampleTypes.unbindSortable();
// Make sure default sorted attributes are added to the table

// Set template group, level and organism
$j('#template_organism').val(data.organism);
$j('#template_level').val(data.level);
$j('#template_parent_id').val(data.template_id);

// Make sure default sorted attributes are added to the table
Templates.table.order([9, "asc"]).draw();
$j.each(Templates.table.rows().data(), (i, row) => {
var newRow = $j(`${attribute_row} tbody`).clone().html();
Expand All @@ -197,6 +242,8 @@ const applyTemplate = () => {
$j(newRow).find(".sample-type-is-title").prop("checked", row[7]);
$j(newRow).find('[data-attr="pid"]').val(row[8]);
$j(newRow).find('[data-attr="isa_tag_id"]').val(row[10]);
$j(newRow).find('[data-attr="isa_tag_title"]').val(row[10]);
$j(newRow).find('[data-attr="isa_tag_title"]').attr('disabled', true);

// Show the CV block if cv_id is not empty
if (row[4]) $j(newRow).find(".controlled-vocab-block").show();
Expand Down Expand Up @@ -238,7 +285,7 @@ const updateTypeSelect = function(field_name) {
} else if (field_name === 'source_sample_type') {
$j("#templates_type_select").val("study source").change();
} else if(field_name === 'sample_type') {
$j("#templates_type_select").val("assay").change();
$j("#templates_type_select").val("assay - material").change();
} else {
$j("#templates_type_select option").first().change();
}
Expand Down
2 changes: 1 addition & 1 deletion app/assets/stylesheets/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@
*= require jquery.splitter/jquery.splitter
*= require select2.min
*= require select2.bootstrap.min
*= require linked_custom_metadata
*= require linked_extended_metadata
*/
Loading

0 comments on commit 8fb0352

Please sign in to comment.