diff --git a/app/components/nextflow/samplesheet/column_component.html.erb b/app/components/nextflow/samplesheet/column_component.html.erb index 278e022ee2..caf265114d 100644 --- a/app/components/nextflow/samplesheet/column_component.html.erb +++ b/app/components/nextflow/samplesheet/column_component.html.erb @@ -57,17 +57,5 @@ id="<%= "metadata-#{header}-column" %>" class="dark:bg-slate-700 bg-slate-50" > - <% samples.each_with_index do |sample, index| %> -
- <%= fields_for "workflow_execution[samples_workflow_executions_attributes][#{index}]" do |s| %> - <%= s.fields_for "samplesheet_params" do |fields| %> - <%= render_cell_type(header, property, sample, fields, index, workflow_params) %> - <% end %> - <% end %> -
- <% end %> diff --git a/app/components/nextflow/samplesheet/column_component.rb b/app/components/nextflow/samplesheet/column_component.rb index 8e01c24508..e2a6afe4c3 100644 --- a/app/components/nextflow/samplesheet/column_component.rb +++ b/app/components/nextflow/samplesheet/column_component.rb @@ -56,7 +56,7 @@ def filter_files_by_pattern(files, pattern) end def render_sample_cell(sample_identifier) - render(Samplesheet::SampleCellComponent.new(sample_identifier:)) + # render(Samplesheet::SampleCellComponent.new(sample_identifier:)) end def render_metadata_cell(sample, name, fields, index) diff --git a/app/components/nextflow/samplesheet/sample_cell_component.rb b/app/components/nextflow/samplesheet/sample_cell_component.rb index 553a659978..d1f399add8 100644 --- a/app/components/nextflow/samplesheet/sample_cell_component.rb +++ b/app/components/nextflow/samplesheet/sample_cell_component.rb @@ -4,12 +4,6 @@ module Nextflow module Samplesheet # Render a single cell of a Nextflow samplesheet for a given sample class SampleCellComponent < Component - erb_template <<~ERB -
- <%= @sample_identifier %> -
- ERB - def initialize(sample_identifier:) @sample_identifier = sample_identifier end diff --git a/app/components/nextflow/samplesheet_component.html.erb b/app/components/nextflow/samplesheet_component.html.erb index 6739dad950..474014d8a3 100644 --- a/app/components/nextflow/samplesheet_component.html.erb +++ b/app/components/nextflow/samplesheet_component.html.erb @@ -2,6 +2,9 @@ +
- + + + + + + + + + + + + diff --git a/app/components/nextflow/samplesheet_component.rb b/app/components/nextflow/samplesheet_component.rb index 42df2c2c50..6a7b8a4910 100644 --- a/app/components/nextflow/samplesheet_component.rb +++ b/app/components/nextflow/samplesheet_component.rb @@ -3,7 +3,8 @@ module Nextflow # Render the contents of a Nextflow samplesheet to a table class SamplesheetComponent < Component - attr_reader :properties, :samples, :required_properties, :metadata_fields, :namespace_id, :workflow_params + attr_reader :properties, :samples, :required_properties, :metadata_fields, :namespace_id, :workflow_params, + :patterns FILE_CELL_TYPES = %w[fastq_cell file_cell].freeze @@ -13,6 +14,7 @@ def initialize(schema:, samples:, fields:, namespace_id:, workflow_params:) @metadata_fields = fields @required_properties = schema['items']['required'] || [] @workflow_params = workflow_params + @patterns = {} extract_properties(schema) end @@ -28,7 +30,8 @@ def samples_workflow_executions_attributes def samples_workflow_execution_attributes(sample) { 'sample_id' => sample.id, - 'samplesheet_params' => sample_samplesheet_params(sample) + 'samplesheet_params' => sample_samplesheet_params(sample), + 'patterns' => {} } end @@ -36,26 +39,53 @@ def sample_samplesheet_params(sample) # rubocop:disable Metrics/CyclomaticComple @properties.to_h do |name, property| case property['cell_type'] when 'sample_cell' - [name, sample.puid] + [name, { form_value: sample.puid, cell_type: 'sample_cell', cell_value: sample.puid }] when 'sample_name_cell' - [name, sample.name] + [name, { form_value: sample.name, cell_type: 'sample_name_cell', cell_value: sample.name }] when 'fastq_cell' - [name, sample.attachments.empty? ? '' : sample.most_recent_fastq_file(name, @workflow_params)[:global_id]] + [name, + file_samplesheet_values(sample.attachments.empty? ? nil : sample.most_recent_fastq_file(name, + @workflow_params))] when 'file_cell' - [name, sample.most_recent_other_file(property['autopopulate'], property['pattern'])[:global_id]] + [name, + file_samplesheet_values(sample.most_recent_other_file(property['autopopulate'], property['pattern']))] when 'metadata_cell' - [name, sample.metadata.fetch(name, '')] + [name, metadata_samplesheet_values(sample, name)] when 'dropdown_cell' || 'input_cell' - [name, ''] + [name, { form_value: '', cell_type: property['cell_type'], cell_value: '' }] end end end + def file_samplesheet_values(file) + { form_value: file.empty? ? '' : file[:global_id], + cell_type: 'file_cell', + cell_value: file.empty? ? I18n.t('nextflow.samplesheet.file_cell_component.no_selected_file') : file[:filename], + attachment_id: file.empty? ? '' : file[:id] } + end + + def metadata_samplesheet_values(sample, name) + metadata = sample.metadata.fetch(name, '') + if metadata.empty? + { form_value: '', + cell_type: 'text', + cell_value: nil } + else + { form_value: metadata, + cell_type: 'metadata_cell', + cell_value: metadata } + end + end + def extract_properties(schema) @properties = schema['items']['properties'] @properties.each do |property, entry| @properties[property]['required'] = schema['items']['required'].include?(property) @properties[property]['cell_type'] = identify_cell_type(property, entry) + if @properties[property]['pattern'] && %w[fastq_cell + file_cell].include?(@properties[property]['cell_type']) + @patterns[property] = @properties[property]['pattern'] + end end if @required_properties.include?('fastq_1') && @required_properties.include?('fastq_2') diff --git a/app/components/nextflow_component.html.erb b/app/components/nextflow_component.html.erb index 57188078d2..8334f8dba9 100644 --- a/app/components/nextflow_component.html.erb +++ b/app/components/nextflow_component.html.erb @@ -3,6 +3,7 @@ data-nextflow--samplesheet-attachments-error-value="<%= t(".attachments_error") %>" data-nextflow--samplesheet-submission-error-value="<%= t(".submission_error") %>" data-nextflow--samplesheet-url-value="<%= url %>" + data-nextflow--samplesheet-workflow-value="<%= {name: @workflow.name, version: @workflow.version }.to_json%>" data-action=" nextflow--autofilled-cell:sendAutofilledInputData->nextflow--samplesheet#updateAutofilledSamplesheetData " diff --git a/app/controllers/workflow_executions/file_selector_controller.rb b/app/controllers/workflow_executions/file_selector_controller.rb index 3867c48253..88fbc63c59 100644 --- a/app/controllers/workflow_executions/file_selector_controller.rb +++ b/app/controllers/workflow_executions/file_selector_controller.rb @@ -8,6 +8,8 @@ class FileSelectorController < ApplicationController before_action :listing_attachments, only: %i[new create] def new + puts 'hihihhihi' + puts file_selector_params render turbo_stream: turbo_stream.update('file_selector_dialog', partial: 'file_selector_dialog', locals: { file_selector_params:, open: true }), status: :ok @@ -33,7 +35,7 @@ def file_selector_params :file_type, required_properties: [], file_selector_arguments: [ - :pattern, { workflow_params: %i[name version] } + { patterns: {} }, { workflow_params: %i[name version] } ] ) end @@ -45,10 +47,10 @@ def listing_attachments # rubocop:disable Metrics/AbcSize file_selector_params['property'], file_selector_params['file_selector_arguments']['workflow_params'] ) when 'other' - @listing_attachments = if file_selector_params['file_selector_arguments']['pattern'] + @listing_attachments = if file_selector_params['file_selector_arguments']['patterns'][file_selector_params['property']] @attachable.filter_files_by_pattern( @attachable.sorted_files[:singles] || [], - file_selector_params['file_selector_arguments']['pattern'] + file_selector_params['file_selector_arguments']['patterns'][file_selector_params['property']] ) else sample.sorted_files[:singles] || [] @@ -77,7 +79,7 @@ def attachments byte_size: attachment.byte_size, created_at: attachment.created_at, metadata: attachment.metadata } - return unless file_selector_params['property'] == 'fastq_1' || file_selector_params['property'] == 'fastq_2' + return unless %w[fastq_1 fastq_2].include?(file_selector_params['property']) assign_associated_attachment_params(attachment) end diff --git a/app/javascript/controllers/nextflow/autofilled_cell_controller.js b/app/javascript/controllers/nextflow/autofilled_cell_controller.js index 3c8f9cebda..f2046232b7 100644 --- a/app/javascript/controllers/nextflow/autofilled_cell_controller.js +++ b/app/javascript/controllers/nextflow/autofilled_cell_controller.js @@ -5,6 +5,7 @@ export default class extends Controller { static values = { inputName: { type: String }, inputValue: { type: String } }; connect() { + console.log("autofilled"); this.sendAutofilledInputData(); } diff --git a/app/javascript/controllers/nextflow/samplesheet_controller.js b/app/javascript/controllers/nextflow/samplesheet_controller.js index bbbfc0a2e6..3b0903b998 100644 --- a/app/javascript/controllers/nextflow/samplesheet_controller.js +++ b/app/javascript/controllers/nextflow/samplesheet_controller.js @@ -9,11 +9,19 @@ export default class extends Controller { "errorMessage", "form", "workflowAttributes", + "samplesheetProperties", + "cellContainer", + "sampleIdentifierCell", + "dropdownCell", + "fileCell", + "metadataCell", + "textCell", ]; static values = { attachmentsError: { type: String }, submissionError: { type: String }, url: { type: String }, + workflow: { type: Object }, }; #error_state = ["border-red-300", "dark:border-red-800"]; @@ -22,32 +30,42 @@ export default class extends Controller { // The samplesheet will use FormData, allowing us to create the inputs of a form without the associated DOM elements. #formData = new FormData(); + #currentPage = 1; + #samplesheetProperties = JSON.parse( + this.samplesheetPropertiesTarget.innerHTML, + ); + #columnNames = Object.keys(this.#samplesheetProperties); connect() { + console.log("properties"); + console.log(this.#samplesheetProperties); if (this.hasWorkflowAttributesTarget) { + this.samplesheetParams = JSON.parse( + this.workflowAttributesTarget.innerText, + ); this.#setInitialSamplesheetData(); + this.#loadPageData(); } } #setInitialSamplesheetData() { - const samples_workflow_attrs = JSON.parse( - this.workflowAttributesTarget.innerText, - ); - for (const index in samples_workflow_attrs) { - for (const sample_attrs in samples_workflow_attrs[index]) { + console.log("params"); + console.log(this.samplesheetParams); + for (const index in this.samplesheetParams) { + for (const sample_attrs in this.samplesheetParams[index]) { if (sample_attrs == "sample_id") { // specifically adds sample to form this.#setFormData( `workflow_execution[samples_workflow_executions_attributes][${index}][${sample_attrs}]`, - samples_workflow_attrs[index][sample_attrs], + this.samplesheetParams[index][sample_attrs], ); continue; } - for (const property in samples_workflow_attrs[index][sample_attrs]) { + for (const property in this.samplesheetParams[index][sample_attrs]) { // adds all remaining sample data to form (files, metadata, etc.) this.#setFormData( `workflow_execution[samples_workflow_executions_attributes][${index}][${sample_attrs}][${property}]`, - samples_workflow_attrs[index][sample_attrs][property], + this.samplesheetParams[index][sample_attrs][property]["form_value"], ); } } @@ -149,4 +167,167 @@ export default class extends Controller { this.errorTarget.classList.remove("hidden"); this.errorMessageTarget.innerHTML = message; } + + #loadPageData() { + let startingIndex = (this.#currentPage - 1) * 5; + this.#columnNames.forEach((columnName) => { + let columnNode = document.getElementById(`metadata-${columnName}-column`); + for (let i = 0; i < 1; i++) { + let container = this.#generateCellContainer(columnNode); + switch (this.#samplesheetProperties[columnName]["cell_type"]) { + case "sample_cell": + case "sample_name_cell": + this.#generateSampleCell(container, columnName, i); + break; + case "dropdown_cell": + this.#generateDropdownCell( + container, + columnName, + i, + this.#samplesheetProperties[columnName]["enum"], + ); + break; + case "fastq_cell": + case "file_cell": + this.#generateFileCell(container, columnName, i); + break; + case "metadata_cell": + this.#generateMetadataCell(container, columnName, i); + break; + case "input_cell": + this.#generateTextCell(container, columnName, i); + break; + default: + this.#generateSampleCell(container, columnName, i); + } + } + }); + // 1 -> 0-4 + // 2 -> 5-9 + // 3 -> 10-14 + // 4 -> 15-19 + } + + // inserting the template html then requerying it out via lastElementChild turns the node from textNode into an + // HTML element we can manipulate via appendChild, insertHTML, etc. + #generateCellContainer(columnNode) { + let newCellContainer = this.cellContainerTarget.innerHTML; + columnNode.insertAdjacentHTML("beforeend", newCellContainer); + return columnNode.lastElementChild; + } + + #generateSampleCell(container, columnName, index) { + let childNode = this.sampleIdentifierCellTarget.innerHTML.replace( + /CELL_PLACEHOLDER/g, + this.samplesheetParams[index]["samplesheet_params"][columnName][ + "cell_value" + ], + ); + container.insertAdjacentHTML("beforeend", childNode); + } + + #generateDropdownCell(container, columnName, index, options) { + let childNode = this.dropdownCellTarget.innerHTML + .replace(/INDEX_PLACEHOLDER/g, index) + .replace(/COLUMN_NAME_PLACEHOLDER/g, columnName); + + container.insertAdjacentHTML("beforeend", childNode); + let select = container.querySelector("select"); + for (let j = 0; j < options.length; j++) { + let option = document.createElement("option"); + option.value = options[j]; + option.innerHTML = options[j]; + select.appendChild(option); + } + // TODO SET SELECTED VALUE + } + + #generateFileCell(container, columnName, index) { + let childNode = this.fileCellTarget.innerHTML + .replace(/INDEX_PLACEHOLDER/g, index) + .replace(/PROPERTY_PLACEHOLDER/g, columnName) + .replace( + /ATTACHABLE_ID_PLACEHOLDER/g, + this.samplesheetParams[index]["sample_id"], + ) + .replace(/ATTACHABLE_TYPE_PLACEHOLDER/g, "Sample") + .replace( + /SELECTED_ID_PLACEHOLDER/g, + this.samplesheetParams[index]["samplesheet_params"][columnName][ + "attachment_id" + ], + ) + .replace( + /FILE_TYPE_PLACEHOLDER/g, + this.#samplesheetProperties[columnName]["cell_type"] == "fastq_cell" + ? "fastq" + : "other", + ) + .replace( + /CELL_VALUE_PLACEHOLDER/g, + this.samplesheetParams[index]["samplesheet_params"][columnName][ + "cell_value" + ], + ); + container.insertAdjacentHTML("beforeend", childNode); + + // sets the verification attribute for required file cells + if (this.#samplesheetProperties[columnName]["required"]) { + let fileNode = document.getElementById( + `${this.samplesheetParams[index]["sample_id"]}_${columnName}`, + ); + fileNode.setAttribute( + "data-file-missing", + this.samplesheetParams[index]["samplesheet_params"][columnName][ + "attachment_id" + ] + ? "false" + : "true", + ); + } + } + + #generateMetadataCell(container, columnName, index) { + if ( + this.samplesheetParams[index]["samplesheet_params"][columnName][ + "form_value" + ] + ) { + let childNode = his.metadataCellTarget.innerHTML.replace( + /METADATA_VALUE_PLACEHOLDER/g, + his.samplesheetParams[index]["samplesheet_params"][columnName][ + "form_value" + ], + ); + container.insertAdjacentHTML("beforeend", childNode); + } else { + this.#generateTextCell(container, columnName, index); + } + } + + #generateTextCell(container, columnName, index) { + let childNode = this.textCellTarget.innerHTML + .replace( + /NAME_PLACEHOLDER/g, + `workflow_execution[samples_workflow_executions_attributes][${index}][samplesheet_params][${columnName}]`, + ) + .replace( + /ID_PLACEHOLDER/g, + `workflow_execution_samples_workflow_executions_attributes_${index}_samplesheet_params_${columnName}`, + ); + + container.insertAdjacentHTML("beforeend", childNode); + const form_value = + this.samplesheetParams[index]["samplesheet_params"][columnName][ + "form_value" + ]; + + if (form_value) { + container.lastElementChild.value = form_value; + } + } } + +// console.log(this.#samplesheetProperties); + +// console.log(this.samplesheetParams); diff --git a/config/pipelines/pipelines.json b/config/pipelines/pipelines.json index 0911993c55..d09a2373c0 100644 --- a/config/pipelines/pipelines.json +++ b/config/pipelines/pipelines.json @@ -19,5 +19,18 @@ "executable": false } ] + }, { + "url": "https://github.com/phac-nml/fastmatchirida", + "name": "phac-nml/fastmatchirida", + "description": "IRIDA Next Fast Match Pipeline", + "versions": [ + { + "name": "0.1.0" + } + ] } + + +, { "url": "https://github.com/phac-nml/mikrokondo", "name": "phac-nml/mikrokondo", "description": "Mikrokondo", "versions": [ { "name": "0.5.0" } ] } + ]