Skip to content

Commit 21c4c5e

Browse files
authored
Merge pull request #2178 from broadinstitute/development
Release 1.86.1
2 parents 2cbc324 + b7a6e83 commit 21c4c5e

14 files changed

Lines changed: 206 additions & 65 deletions

File tree

Gemfile.lock

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ GEM
255255
listen (3.5.1)
256256
rb-fsevent (~> 0.10, >= 0.10.3)
257257
rb-inotify (~> 0.9, >= 0.9.10)
258-
loofah (2.22.0)
258+
loofah (2.23.1)
259259
crass (~> 1.0.2)
260260
nokogiri (>= 1.12.0)
261261
macaddr (1.7.2)
@@ -326,11 +326,11 @@ GEM
326326
net-protocol
327327
netrc (0.11.0)
328328
nio4r (2.7.3)
329-
nokogiri (1.16.6-arm64-darwin)
329+
nokogiri (1.16.8-arm64-darwin)
330330
racc (~> 1.4)
331-
nokogiri (1.16.6-x86_64-darwin)
331+
nokogiri (1.16.8-x86_64-darwin)
332332
racc (~> 1.4)
333-
nokogiri (1.16.6-x86_64-linux)
333+
nokogiri (1.16.8-x86_64-linux)
334334
racc (~> 1.4)
335335
oauth2 (1.4.7)
336336
faraday (>= 0.8, < 2.0)
@@ -364,7 +364,7 @@ GEM
364364
public_suffix (5.0.4)
365365
puma (5.6.9)
366366
nio4r (~> 2.0)
367-
racc (1.8.0)
367+
racc (1.8.1)
368368
rack (2.2.9)
369369
rack-brotli (1.1.0)
370370
brotli (>= 0.1.7)
@@ -396,9 +396,9 @@ GEM
396396
activesupport (>= 5.0.0)
397397
minitest
398398
nokogiri (>= 1.6)
399-
rails-html-sanitizer (1.6.0)
399+
rails-html-sanitizer (1.6.1)
400400
loofah (~> 2.21)
401-
nokogiri (~> 1.14)
401+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
402402
railties (6.1.7.8)
403403
actionpack (= 6.1.7.8)
404404
activesupport (= 6.1.7.8)

app/controllers/api/v1/study_files_controller.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,9 @@ def perform_update!(study_file)
402402
end
403403
end
404404

405-
if safe_file_params[:upload].present? && !is_chunked || safe_file_params[:remote_location].present?
405+
if safe_file_params[:upload].present? && !is_chunked ||
406+
safe_file_params[:remote_location].present? ||
407+
study_file.needs_raw_counts_extraction?
406408
complete_upload_process(study_file, parse_on_upload)
407409
end
408410
end
@@ -445,7 +447,7 @@ def chunk
445447
def complete_upload_process(study_file, parse_on_upload)
446448
# set status to uploaded on full create, unless file is parsed and this is just an update
447449
study_file.update!(status: 'uploaded', parse_status: 'unparsed') unless study_file.parsed?
448-
if parse_on_upload
450+
if parse_on_upload || study_file.needs_raw_counts_extraction?
449451
if study_file.parseable?
450452
persist_on_fail = study_file.remote_location.present?
451453
FileParseService.run_parse_job(@study_file, @study, current_api_user, persist_on_fail:)

app/javascript/components/upload/ExpressionFileForm.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ export default function ExpressionFileForm({
3939
const speciesOptions = fileMenuOptions.species.map(spec => ({ label: spec.common_name, value: spec.id }))
4040
const selectedSpecies = speciesOptions.find(opt => opt.value === file.taxon_id)
4141
const isMtxFile = file.file_type === 'MM Coordinate Matrix'
42-
const rawCountsInfo = file.expression_file_info.is_raw_counts
43-
const isRawCountsFile = rawCountsInfo === 'true' || rawCountsInfo
42+
const rawCountsInfo = file.expression_file_info.is_raw_counts
43+
const isRawCountsFile = rawCountsInfo === 'true' || rawCountsInfo === true
4444
const [showRawCountsUnits, setShowRawCountsUnits] = useState(isRawCountsFile)
4545

4646
const allowedFileExts = isMtxFile ? FileTypeExtensions.mtx : FileTypeExtensions.plainText
4747
let requiredFields = showRawCountsUnits ? RAW_COUNTS_REQUIRED_FIELDS : REQUIRED_FIELDS
48-
const rawCountsRequired = featureFlagState && featureFlagState.raw_counts_required_frontend
48+
const rawCountsRequired = featureFlagState && featureFlagState.raw_counts_required_frontend && !isAnnDataExperience
4949
if (rawCountsRequired && !isRawCountsFile ) {
5050
requiredFields = requiredFields.concat(PROCESSED_ASSOCIATION_FIELD)
5151
}

app/lib/file_parse_service.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ def self.run_parse_job(study_file, study, user, reparse: false, persist_on_fail:
110110
anndata_file: study_file.gs_url, extract: %w[cluster], obsm_keys: [obsm_key],
111111
file_size: study_file.upload_file_size
112112
)
113+
elsif study_file.needs_raw_counts_extraction?
114+
params_object = AnnDataIngestParameters.new(
115+
anndata_file: study_file.gs_url, extract: %w[raw_counts], obsm_keys: nil,
116+
file_size: study_file.upload_file_size
117+
)
113118
else
114119
params_object = AnnDataIngestParameters.new(
115120
anndata_file: study_file.gs_url, obsm_keys: study_file.ann_data_file_info.obsm_key_names,

app/models/ingest_job.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -838,8 +838,14 @@ def launch_anndata_subparse_jobs
838838
job.delay.push_remote_and_launch_ingest
839839
end
840840

841-
# unset anndata_summary flag to allow reporting summary later
842-
study_file.unset_anndata_summary!
841+
# if this was only a raw counts extraction, update parse status
842+
if params_object.extract == %w[raw_counts]
843+
study_file.update(parse_status: 'parsed')
844+
launch_differential_expression_jobs
845+
else
846+
# unset anndata_summary flag to allow reporting summary later unless this is only a raw counts extraction
847+
study_file.unset_anndata_summary!
848+
end
843849
end
844850
end
845851

@@ -1017,7 +1023,7 @@ def log_to_mixpanel
10171023
mixpanel_log_props = get_job_analytics
10181024
# log job properties to Mixpanel
10191025
MetricsService.log(mixpanel_event_name, mixpanel_log_props, user)
1020-
report_anndata_summary if study_file.is_viz_anndata? && action != :ingest_anndata
1026+
report_anndata_summary if study_file.is_viz_anndata? && !%i[ingest_anndata differential_expression].include?(action)
10211027
end
10221028

10231029
# set a mixpanel event name based on action

app/models/study_file.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,11 @@ def is_raw_counts_file?
10471047
!!expression_file_info&.is_raw_counts || !!ann_data_file_info&.has_raw_counts
10481048
end
10491049

1050+
# check for when a user comes back to indicate an AnnData file has raw counts data
1051+
def needs_raw_counts_extraction?
1052+
is_viz_anndata? && is_raw_counts_file? && parsed? && study.expression_matrix_cells(self, matrix_type: 'raw').empty?
1053+
end
1054+
10501055
def is_anndata?
10511056
file_type == 'AnnData'
10521057
end

config/application.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Application < Rails::Application
2929
config.middleware.use Rack::Brotli
3030

3131
# Docker image for file parsing via scp-ingest-pipeline
32-
config.ingest_docker_image = 'gcr.io/broad-singlecellportal-staging/scp-ingest-pipeline:1.37.0'
32+
config.ingest_docker_image = 'gcr.io/broad-singlecellportal-staging/scp-ingest-pipeline:1.37.1'
3333

3434
# Docker image for image pipeline jobs
3535
config.image_pipeline_docker_image = 'gcr.io/broad-singlecellportal-staging/image-pipeline:0.1.0_c2b090043'

image-pipeline/yarn.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -970,9 +970,9 @@ cross-fetch@3.1.5:
970970
node-fetch "2.6.7"
971971

972972
cross-spawn@^6.0.5:
973-
version "6.0.5"
974-
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
975-
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
973+
version "6.0.6"
974+
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57"
975+
integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==
976976
dependencies:
977977
nice-try "^1.0.4"
978978
path-key "^2.0.1"

test/api/study_files_controller_test.rb

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,4 +291,65 @@ class StudyFilesControllerTest < ActionDispatch::IntegrationTest
291291
assert_equal description, study_file.description
292292
assert study_file.parsed?
293293
end
294+
295+
test 'should extract only raw counts from existing AnnData uploads' do
296+
study = FactoryBot.create(:detached_study,
297+
name_prefix: 'AnnData Raw Counts Study',
298+
public: true,
299+
user: @user,
300+
test_array: @@studies_to_clean)
301+
ann_data_file = FactoryBot.create(:ann_data_file,
302+
name: 'matrix.h5ad',
303+
study:,
304+
upload_file_size: 1.megabyte,
305+
cell_input: %w[A B C D],
306+
has_raw_counts: false,
307+
reference_file: false,
308+
annotation_input: [
309+
{ name: 'disease', type: 'group', values: %w[cancer cancer normal normal] }
310+
],
311+
coordinate_input: [
312+
{ umap: { x: [1, 2, 3, 4], y: [5, 6, 7, 8] } }
313+
],
314+
expression_input: {
315+
'phex' => [['A', 0.3], ['B', 1.0], ['C', 0.5], ['D', 0.1]]
316+
})
317+
318+
assert_not ann_data_file.is_raw_counts_file?
319+
assert_not ann_data_file.needs_raw_counts_extraction?
320+
321+
# test for normal uploads
322+
study_file_attributes = {
323+
study_file: {
324+
expression_file_info_attributes: {
325+
is_raw_counts: true,
326+
units: 'raw counts'
327+
}
328+
}
329+
}
330+
FileParseService.stub :run_parse_job, true do
331+
execute_http_request(:patch, api_v1_study_study_file_path(study_id: study.id, id: ann_data_file.id),
332+
request_payload: study_file_attributes)
333+
assert_response :success
334+
ann_data_file.reload
335+
assert ann_data_file.is_raw_counts_file?
336+
assert ann_data_file.needs_raw_counts_extraction?
337+
end
338+
339+
# test for bucket path uploads, resetting flags first
340+
ann_data_file.expression_file_info.update(is_raw_counts: false)
341+
ann_data_file.ann_data_file_info.update(has_raw_counts: false)
342+
ann_data_file.update(remote_location: 'matrix.h5ad')
343+
assert_not ann_data_file.is_raw_counts_file?
344+
assert_not ann_data_file.needs_raw_counts_extraction?
345+
346+
FileParseService.stub :run_parse_job, true do
347+
execute_http_request(:patch, api_v1_study_study_file_path(study_id: study.id, id: ann_data_file.id),
348+
request_payload: study_file_attributes)
349+
assert_response :success
350+
ann_data_file.reload
351+
assert ann_data_file.is_raw_counts_file?
352+
assert ann_data_file.needs_raw_counts_extraction?
353+
end
354+
end
294355
end

test/factories/study_file.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@
153153
file_type { 'AnnData' }
154154
parse_status { 'parsed' }
155155
transient do
156-
reference_file { true }
157156
# cell_input is an array of all cell names
158157
# e.g. ['cellA', 'cellB', 'cellC']
159158
cell_input { [] }
@@ -173,7 +172,8 @@
173172
# phex: [['cellA', 0.6],['cellB', 6.1], ['cellC', 4.5]]
174173
# }
175174
expression_input { {} }
176-
has_raw_counts { true }
175+
has_raw_counts { expression_input.any? && cell_input.any? }
176+
reference_file { cell_input.empty? && coordinate_input.empty? && annotation_input.empty? && expression_input.empty? }
177177
end
178178
after(:create) do |file, evaluator|
179179
file.build_ann_data_file_info
@@ -194,10 +194,16 @@
194194
end
195195
if evaluator.expression_input.any?
196196
file.build_expression_file_info(library_preparation_protocol: "10x 5' v3")
197-
file.expression_file_info.save
198197
file.ann_data_file_info.has_expression = true
199-
file.ann_data_file_info.has_raw_counts = evaluator.has_raw_counts
200-
%w[raw processed].each do |matrix_type|
198+
if evaluator.has_raw_counts
199+
file.expression_file_info.is_raw_counts = evaluator.has_raw_counts
200+
file.expression_file_info.units = 'raw counts'
201+
file.ann_data_file_info.has_raw_counts = evaluator.has_raw_counts
202+
end
203+
file.expression_file_info.save
204+
matrix_types = %w[processed]
205+
matrix_types << 'raw' if evaluator.has_raw_counts
206+
matrix_types.each do |matrix_type|
201207
FactoryBot.create(:data_array,
202208
array_type: 'cells',
203209
array_index: 0,

0 commit comments

Comments
 (0)