Class: Study
- Inherits:
-
ApplicationRecord
show all
- Extended by:
- Attributable::Association::Target, EventfulRecord, Metadata
- Includes:
- AASM, Api::StudyIo::Extensions, Commentable, DataRelease, EventfulRecord, ModelExtensions::Study, ReferenceGenome::Associations, Role::Authorized, SampleManifest::Associations, SharedBehaviour::Named, StudyReport::StudyDetails, Uuid::Uuidable
- Defined in:
- app/models/study.rb
Overview
Note:
This is really quite convoluted, and couples together administrative organization alongside accessioning and data-access rules. It results in samples being tied to an EGAS/ERP far too early in their lifecycle, and as a result we often need to perform 'sample moves'. Although we do need to know if samples are open(ENA) or managed(EGA) at the point of accessioning.
A Study is a collection of various samples and the work done on them. They are perhaps slightly overloaded, and provide: - A means of grouping together samples for administrative purposes - A means of generating EGAS/ERP study accession numbers at the ENA/EGA - @see Accessionable::Study - These accession numbers are used at data release to group samples together for publication - For managed/EGA studies, also ties the data to an Accessionable::Dac and Accessionable::Policy - A means of generating the aforementioned Accessionable::Dac and Accessionable::Policy @note These should DEFINITELY be separate entities - A means of tying data to internal data-release timings - A means to apply internal data access policies to released sequencing data - A means to tie interested parties to the samples and the work done on them - A way of specifying common ways of filtering/processing generated data. eg. filter human sequence - The service with which a Sample will be accessioned (eg. EGA/ENA)
When a Sample enters Sequencescape it will usually be associated with a single Study, usually determined by the Study associated with the SampleManifest. This study will be recorded on the Aliquot in the stock Receptacle, and additionally a StudySample will record this association.
When work is requested an Order will be created, specifying a list of receptacles and the Study for which this work is being performed. This will set initial study id on request and in turn will be recorded on any downstream aliquots. Critically, it is the study specified on the Aliquot in the Lane which will influence processes like data release and data access.
Defined Under Namespace
Classes: Metadata
Constant Summary
collapse
- STOCK_PLATE_PURPOSES =
['Stock Plate', 'Stock RNA Plate'].freeze
- YES =
'Yes'
- NO =
'No'
- YES_OR_NO =
[YES, NO].freeze
- Other_type =
'Other'
- STUDY_SRA_HOLDS =
%w[Hold Public].freeze
- DATA_RELEASE_STRATEGY_OPEN =
'open'
- DATA_RELEASE_STRATEGY_MANAGED =
'managed'
- DATA_RELEASE_STRATEGY_NOT_APPLICABLE =
'not applicable'
- DATA_RELEASE_STRATEGIES =
[
DATA_RELEASE_STRATEGY_OPEN,
DATA_RELEASE_STRATEGY_MANAGED,
DATA_RELEASE_STRATEGY_NOT_APPLICABLE
].freeze
- DATA_RELEASE_TIMING_STANDARD =
'standard'
- DATA_RELEASE_TIMING_NEVER =
'never'
- DATA_RELEASE_TIMING_DELAYED =
'delayed'
- DATA_RELEASE_TIMING_IMMEDIATE =
'immediate'
- DATA_RELEASE_TIMINGS =
[
DATA_RELEASE_TIMING_STANDARD,
DATA_RELEASE_TIMING_IMMEDIATE,
DATA_RELEASE_TIMING_DELAYED
].freeze
- OLD_DATA_RELEASE_PREVENTION_REASONS =
['data validity', 'legal', 'replication of data subset'].freeze
- DATA_RELEASE_PREVENTION_REASON_OTHER =
'Other (please specify)'
- DATA_RELEASE_PREVENTION_REASONS =
[
'Pilot or validation studies - DAC approval not required',
'Collaborators will share data in a research repository - DAC approval not required',
'Prevent harm (e.g sensitive studies or biosecurity) - DAC approval required',
'Protecting IP - DAC approval required',
DATA_RELEASE_PREVENTION_REASON_OTHER
].freeze
- OLD_DATA_RELEASE_DELAY_FOR_OTHER =
'other'
- DATA_RELEASE_DELAY_FOR_OTHER =
'Other (please specify below)'
- OLD_DATA_RELEASE_DELAY_REASONS =
['other', 'phd study'].freeze
- DATA_RELEASE_DELAY_REASONS_STANDARD =
[
'PhD study',
'Capacity building',
'Intellectual property protection',
'Additional time to make data FAIR',
DATA_RELEASE_DELAY_FOR_OTHER
].freeze
- DATA_RELEASE_DELAY_REASONS_ASSAY =
['assay of no other use'].freeze
- DATA_RELEASE_DELAY_PERIODS =
['3 months', '6 months', '9 months', '12 months', '18 months'].freeze
Constants included
from Metadata
Metadata::SECTION_FIELDS
StudyReport::StudyDetails::BATCH_SIZE
Instance Attribute Summary collapse
Instance Method Summary
collapse
has_many_events, has_many_lab_events, has_one_event_with_family
Methods included from Metadata
has_metadata
default, extended, for_select_association
included
included
included
#after_comment_addition
#accession_required?, #do_not_enforce_accessioning, #for_array_express?, #valid_data_release_properties?
included, #unsaved_uuid!, #uuid
included, #render_class
#each_stock_well_id_in_study_in_batches, #progress_report_header, #progress_report_on_all_assets
alias_association, convert_labware_to_receptacle_for, find_by_id_or_name, find_by_id_or_name!
Methods included from Squishify
extended
Instance Attribute Details
#approval ⇒ Object
Returns the value of attribute approval.
113
114
115
|
# File 'app/models/study.rb', line 113
def approval
@approval
end
|
#run_count ⇒ Object
Returns the value of attribute run_count.
113
114
115
|
# File 'app/models/study.rb', line 113
def run_count
@run_count
end
|
#total_price ⇒ Object
Returns the value of attribute total_price.
113
114
115
|
# File 'app/models/study.rb', line 113
def total_price
@total_price
end
|
Instance Method Details
#abbreviation ⇒ Object
529
530
531
532
|
# File 'app/models/study.rb', line 529
def abbreviation
abbreviation = study_metadata.study_name_abbreviation
abbreviation.presence || "#{id}STDY"
end
|
#accession_all_samples ⇒ Object
525
526
527
|
# File 'app/models/study.rb', line 525
def accession_all_samples
samples.find_each(&:accession) if accession_number?
end
|
#accession_number? ⇒ Boolean
521
522
523
|
# File 'app/models/study.rb', line 521
def accession_number?
ebi_accession_number.present?
end
|
#accession_service ⇒ Object
#approved? ⇒ Boolean
538
539
540
541
|
# File 'app/models/study.rb', line 538
def approved?
true
end
|
#asset_progress(assets = nil) {|initial_requests.asset_statistics(wheres)| ... } ⇒ Object
Yields information on the state of all assets in a convenient fashion for displaying in a table.
459
460
461
462
463
|
# File 'app/models/study.rb', line 459
def asset_progress(assets = nil)
wheres = {}
wheres = { asset_id: assets.map(&:id) } if assets.present?
yield(initial_requests.asset_statistics(wheres))
end
|
#completed ⇒ Object
444
445
446
447
448
449
450
|
# File 'app/models/study.rb', line 444
def completed
counts = requests.standard.group('state').count
total = counts.values.sum
failed = counts['failed'] || 0
cancelled = counts['cancelled'] || 0
(total - failed - cancelled) > 0 ? (counts.fetch('passed', 0) * 100) / (total - failed - cancelled) : 0
end
|
#dac_accession_number ⇒ Object
513
514
515
|
# File 'app/models/study.rb', line 513
def dac_accession_number
study_metadata.ega_dac_accession_number
end
|
#dac_refname ⇒ Object
482
483
484
|
# File 'app/models/study.rb', line 482
def dac_refname
"DAC for study - #{name} - ##{id}"
end
|
#data_release_delay_options(assay_option: false) ⇒ Array<String>
Helper method for edit dropdowns to support backwards compatibility with old options.
#data_release_prevention_options ⇒ Array<String>
Helper method for edit dropdowns to support backwards compatibility with old options.
583
584
585
586
587
588
589
590
|
# File 'app/models/study.rb', line 583
def data_release_prevention_options
additional_options = []
if OLD_DATA_RELEASE_PREVENTION_REASONS.include? study_metadata.data_release_prevention_reason
additional_options << study_metadata.data_release_prevention_reason
end
DATA_RELEASE_PREVENTION_REASONS + additional_options
end
|
#dehumanise_abbreviated_name ⇒ Object
534
535
536
|
# File 'app/models/study.rb', line 534
def dehumanise_abbreviated_name
abbreviation.downcase.gsub(/ +/, '_')
end
|
#each_well_for_qc_report_in_batches(exclude_existing, product_criteria, plate_purposes = nil) ⇒ Object
409
410
411
412
413
414
415
416
417
418
419
420
421
|
# File 'app/models/study.rb', line 409
def each_well_for_qc_report_in_batches(exclude_existing, product_criteria, plate_purposes = nil)
base_scope =
Well
.on_plate_purpose_included(PlatePurpose.where(name: plate_purposes || STOCK_PLATE_PURPOSES))
.for_study_through_aliquot(self)
.without_blank_samples
.includes(:well_attribute, :aliquots, :map, samples: :sample_metadata)
.readonly(true)
scope = exclude_existing ? base_scope.without_report(product_criteria) : base_scope
scope.find_in_batches { |wells| yield wells }
end
|
#ebi_accession_number ⇒ Object
509
510
511
|
# File 'app/models/study.rb', line 509
def ebi_accession_number
study_metadata.study_ebi_accession_number
end
|
#ethical_approval_required? ⇒ Boolean
543
544
545
546
|
# File 'app/models/study.rb', line 543
def ethical_approval_required?
study_metadata.contains_human_dna == Study::YES && study_metadata.contaminated_human_dna == Study::NO &&
study_metadata.commercially_available == Study::NO
end
|
#locale ⇒ Object
505
506
507
|
# File 'app/models/study.rb', line 505
def locale
funding_source
end
|
#mailing_list_of_managers ⇒ Object
567
568
569
570
|
# File 'app/models/study.rb', line 567
def mailing_list_of_managers
configured_managers = managers.pluck(:email).compact.uniq
configured_managers.empty? ? configatron.fetch(:ssr_emails, User.all_administrators_emails) : configured_managers
end
|
#mark_active ⇒ Object
436
437
438
|
# File 'app/models/study.rb', line 436
def mark_active
logger.warn "Study activation failed! #{errors.map(&:to_s)}" unless active?
end
|
#mark_deactive ⇒ Object
432
433
434
|
# File 'app/models/study.rb', line 432
def mark_deactive
logger.warn "Study deactivation failed! #{errors.map(&:to_s)}" unless inactive?
end
|
#owner ⇒ Object
Returns the study owner (user) if exists or nil TODO - Should be “owners” and return all owners or empty array - done TODO - Look into this is the person that created it really the owner? If so, then an owner should be created when a study is created.
501
502
503
|
# File 'app/models/study.rb', line 501
def owner
owners.first
end
|
#policy_accession_number ⇒ Object
517
518
519
|
# File 'app/models/study.rb', line 517
def policy_accession_number
study_metadata.ega_policy_accession_number
end
|
#rebroadcast ⇒ Object
576
577
578
|
# File 'app/models/study.rb', line 576
def rebroadcast
broadcast
end
|
#request_progress {|@stats_cache ||= initial_requests.progress_statistics| ... } ⇒ Object
Yields information on the state of all request types in a convenient fashion for displaying in a table. Used initial requests, which won't capture cross study sequencing requests.
454
455
456
|
# File 'app/models/study.rb', line 454
def request_progress
yield(@stats_cache ||= initial_requests.progress_statistics) if block_given?
end
|
#sample_progress(samples = nil) ⇒ Object
Yields information on the state of all samples in a convenient fashion for displaying in a table.
466
467
468
469
470
471
472
473
474
475
476
|
# File 'app/models/study.rb', line 466
def sample_progress(samples = nil)
if samples.blank?
requests.sample_statistics_new
else
yield(requests.where(aliquots: { sample_id: samples.pluck(:id) }).sample_statistics_new)
end
end
|
#send_samples_to_service? ⇒ Boolean
559
560
561
|
# File 'app/models/study.rb', line 559
def send_samples_to_service?
accession_service.no_study_accession_needed || (!study_metadata.never_release? && accession_number?)
end
|
#study ⇒ Object
492
493
494
|
# File 'app/models/study.rb', line 492
def study
self
end
|
#study_status ⇒ Object
478
479
480
|
# File 'app/models/study.rb', line 478
def study_status
inactive? ? 'closed' : 'open'
end
|
#subject_type ⇒ Object
572
573
574
|
# File 'app/models/study.rb', line 572
def subject_type
'study'
end
|
440
441
442
|
# File 'app/models/study.rb', line 440
def
.each_with_object([]) { |c, array| array << c.description if c.description.present? }.join(', ')
end
|
#unprocessed_submissions? ⇒ Boolean
486
487
488
489
|
# File 'app/models/study.rb', line 486
def unprocessed_submissions?
study.orders.any? { |o| o.submission.nil? || o.submission.unprocessed? }
end
|
#validate_ena_required_fields! ⇒ Object
563
564
565
|
# File 'app/models/study.rb', line 563
def validate_ena_required_fields!
valid?(:accession) or raise ActiveRecord::RecordInvalid, self
end
|
#validate_ethically_approved ⇒ Object
396
397
398
399
400
401
402
403
404
405
406
407
|
# File 'app/models/study.rb', line 396
def validate_ethically_approved
return true if valid_ethically_approved?
message =
if ethical_approval_required?
'should be either true or false for this study.'
else
'should be not applicable (null) not false.'
end
errors.add(:ethically_approved, message)
false
end
|
#warnings ⇒ Object
423
424
425
426
427
428
429
430
|
# File 'app/models/study.rb', line 423
def warnings
if study_metadata.managed? && study_metadata.data_access_group.blank?
'No user group specified for a managed study. Please specify a valid Unix user group to ensure study data is visible to the correct people.'
end
end
|