Skip to content

Commit

Permalink
Specific purposes nih tables exporting (#1959)
Browse files Browse the repository at this point in the history
* Specific purposes nih tables exporting

* Fixes PR review

* Lint fixes

* Fixes PR review
  • Loading branch information
leandroradusky authored Jul 7, 2023
1 parent 71a240b commit b3f1f91
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 10 deletions.
5 changes: 3 additions & 2 deletions app/controllers/nih_tables_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class NihTablesController < ApplicationController
def show
@samples_report = SamplesReport.find(params[:id])
@target_batch = @samples_report.target_batch
return unless authorize_resource(@samples_report, READ_SAMPLES_REPORT)

zip_data = create_zip_file
Expand All @@ -21,9 +22,9 @@ def create_zip_file
add_nih_table('results', stream)

if purpose == "LOD"
#add_nih_table('lod', stream)
add_nih_table('lod', stream)
elsif purpose == "Challenge"
#add_nih_table('challenge', stream)
add_nih_table('challenge', stream)
end
end
zip_stream.rewind
Expand Down
5 changes: 5 additions & 0 deletions app/models/samples_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ def calculate_lod_and_lob
self.save
end

def target_batch
# The target batch for this box is the batch of any sample which distractor is false or null
samples_report_samples.joins(:sample).find_by("samples.distractor IS NULL OR samples.distractor = 'false'").sample.batch
end

private

def there_are_samples
Expand Down
54 changes: 54 additions & 0 deletions app/views/samples_reports/nih_challenge.csv.csvbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
csv << [
'protocol_id',
'technology_platform',
'vqa_box_id',
'sample_group',
'target_analyte_type',
'target_organism_name',
'target_organism_taxonomy_id',
'pango_lineage',
'who_label',
'non_clinical_cutoff_min',
'non_clinical_cutoff_unit',
'true_positives',
'false_positives',
'true_negatives',
'false_negatives',
'non_clinical_sensitivity',
'non_clinical_sensitivity_ci_percent',
'non_clinical_sensitivity_ci_min',
'non_clinical_specificity',
'non_clinical_specificity_ci_percent',
'non_clinical_specificity_ci_min',
'area_under_the_roc_curve',
'roc_x_values',
'roc_y_values'
]

sample = @samples_report.samples[0]
csv << [
nil,
nil,
sample.box.uuid,
"#{sample.box.purpose}-panel",
'inactivated virus',
'SARS-CoV-2',
@target_batch.target_organism_taxonomy_id,
@target_batch.pango_lineage,
@target_batch.who_label,
@samples_report.threshold,
'copies/ml',
params[:true_positives],
params[:false_positives],
params[:true_negatives],
params[:false_negatives],
params[:tpr],
nil,
nil,
params[:fpr],
nil,
nil,
params[:auc],
params[:roc_x_values],
params[:roc_y_values]
]
44 changes: 44 additions & 0 deletions app/views/samples_reports/nih_lod.csv.csvbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
csv << [
'protocol_id',
'technology_platform',
'vqa_box_id',
'sample_group',
'target_analyte_type',
'target_organism_name',
'target_organism_taxonomy_id',
'pango_lineage',
'who_label',
'limit_of_blank',
'limit_of_blank_unit',
'limit_of_detection',
'limit_of_detection_reference_gene',
'limit_of_detection_unit',
'limit_of_detection_sn_ratio',
'limit_of_detection_ci_percent',
'limit_of_detection_detail',
'analytical_sensitivity',
'analytical_sensitivity_unit'
]

sample = @samples_report.samples[0]
csv << [
nil,
nil,
sample.box.uuid,
"#{sample.box.purpose}-panel",
'inactivated virus',
'SARS-CoV-2',
@target_batch.target_organism_taxonomy_id,
@target_batch.pango_lineage,
@target_batch.who_label,
@samples_report.lob,
nil,
@samples_report.lod,
@target_batch.reference_gene,
nil,
nil,
"90",
'linear regression',
nil,
nil
]
39 changes: 33 additions & 6 deletions app/views/samples_reports/show.haml
Original file line number Diff line number Diff line change
Expand Up @@ -294,14 +294,41 @@
document.querySelector(`.nih-report-type-modal-container`).classList.add("hidden");
}

function downloadNIHFiles() {
async function downloadNIHFiles() {
hideConfirmModal();

// Open the download_nih_tables path in a new tab
var context = getUrlParameter('context');
var url = '/samples_reports/'+#{@samples_report.id}+'/nih_tables?context='+context+"";
window.open(url, '_blank');


// if purpose is challenge, then we need the live threshold calculations
if (#{@purpose.to_json} == "Challenge") {
var threshold = document.getElementById("threshold").value;
var url = '/samples_reports/update_threshold?context='+context+"&samples_report_id=#{@samples_report.id}&threshold="+threshold;
await fetch(url)
.then((response) => response.json())
.then((ret) => {
var params = {
true_positives: ret.confusion_matrix.true_positive || 0,
true_negatives: ret.confusion_matrix.true_negative || 0,
false_positives: ret.confusion_matrix.false_positive || 0,
false_negatives: ret.confusion_matrix.false_negative || 0,
tpr: (ret.confusion_matrix.true_positive || 0) / ((ret.confusion_matrix.true_positive || 0) + (ret.confusion_matrix.false_negative || 0)),
fpr: (ret.confusion_matrix.false_positive || 0) / ((ret.confusion_matrix.false_positive || 0) + (ret.confusion_matrix.true_negative || 0)),
auc: #{auc(roc_curve(@samples_report))},
roc_x_values: #{roc_curve(@samples_report).map{|x| x[0]}},
roc_y_values: #{roc_curve(@samples_report).map{|x| x[1]}},
};

// create url params with the params dictionary
var urlParams = new URLSearchParams(Object.entries(params));

// Open the download_nih_tables path in a new tab
var url = '/samples_reports/'+#{@samples_report.id}+'/nih_tables?context='+context+"&"+urlParams.toString();
window.open(url, '_blank');
})
}
else {
var url = '/samples_reports/'+#{@samples_report.id}+'/nih_tables?context='+context;
window.open(url, '_blank');
}
}

document.getElementById("print-as-csv").addEventListener("click", showConfirmModal);
Expand Down
47 changes: 45 additions & 2 deletions spec/controllers/nih_tables_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
@box = Box.make!(:overfilled, institution: @institution, purpose: "LOD")
@samples_report = SamplesReport.create(institution: @institution, samples_report_samples: @box.samples.map{|s| SamplesReportSample.new(sample: s)}, name: "Test")

@box2 = Box.make!(:overfilled, institution: @institution, purpose: "Challenge")
@samples_report2 = SamplesReport.create(institution: @institution, samples_report_samples: @box2.samples.map{|s| SamplesReportSample.new(sample: s)}, name: "TestChallenge")

grant @user, @other_user, @other_institution, READ_INSTITUTION
end

Expand Down Expand Up @@ -49,7 +52,6 @@
samples_table = CSV.parse(Zip::File.open_buffer(response.body).entries.find{|e| e.name == "Test_samples.csv"}.get_input_stream.read, headers: true)

expect(samples_table.count).to eq(@samples_report.samples_report_samples.count)

expect(samples_table["sample_id"]).to eq(@samples_report.samples_report_samples.map{|srs| srs.sample.id.to_s})
end

Expand All @@ -59,10 +61,51 @@
samples_table = CSV.parse(Zip::File.open_buffer(response.body).entries.find{|e| e.name == "Test_results.csv"}.get_input_stream.read, headers: true)

expect(samples_table.count).to eq(@samples_report.samples_report_samples.count)

expect(samples_table["sample_id"]).to eq(@samples_report.samples_report_samples.map{|srs| srs.sample.id.to_s})
end

it "should contain the LOD table for if the box purpose is LOD" do
get :show, params: { id: @samples_report.id }

expect(Zip::File.open_buffer(response.body).entries.map(&:name)).to include("Test_lod.csv")
end

it "should not contain the LOD table for if the box purpose is Challenge" do
get :show, params: { id: @samples_report2.id }

expect(Zip::File.open_buffer(response.body).entries.map(&:name)).not_to include("TestChallenge_lod.csv")
end

it "should contain the LOD table with the correct data" do
get :show, params: { id: @samples_report.id }

lod_table = CSV.parse(Zip::File.open_buffer(response.body).entries.find{|e| e.name == "Test_lod.csv"}.get_input_stream.read, headers: true)

expect(lod_table.count).to eq(1)
expect(lod_table["vqa_box_id"]).to include(@box.uuid)
end

it "should contain the Challenge table for if the box purpose is Challenge" do
get :show, params: { id: @samples_report2.id }

expect(Zip::File.open_buffer(response.body).entries.map(&:name)).to include("TestChallenge_challenge.csv")
end

it "should not contain the Challenge table for if the box purpose is LOD" do
get :show, params: { id: @samples_report.id }

expect(Zip::File.open_buffer(response.body).entries.map(&:name)).not_to include("Test_challenge.csv")
end

it "should contain the Challenge table with the correct data" do
get :show, params: { id: @samples_report2.id }

challenge_table = CSV.parse(Zip::File.open_buffer(response.body).entries.find{|e| e.name == "TestChallenge_challenge.csv"}.get_input_stream.read, headers: true)

expect(challenge_table.count).to eq(1)
expect(challenge_table["vqa_box_id"]).to include(@box2.uuid)
end

it "should not allow access to user without read access" do
sign_in @other_user
get :show, params: { id: @samples_report.id, context: @other_institution.uuid }
Expand Down

0 comments on commit b3f1f91

Please sign in to comment.