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_TIMING_PUBLICATION =
'delay until publication'
- 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
- EBI_LIBRARY_STRATEGY_OPTIONS =
- EBI_LIBRARY_SOURCE_OPTIONS =
- EBI_LIBRARY_SELECTION_OPTIONS =
- REMAPPED_ATTRIBUTES =
{
contaminated_human_dna: YES_OR_NO,
remove_x_and_autosomes: YES_OR_NO,
study_sra_hold: STUDY_SRA_HOLDS,
contains_human_dna: YES_OR_NO,
commercially_available: YES_OR_NO
}.transform_values { |v| v.index_by { |b| b.downcase } }
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.
128
129
130
|
# File 'app/models/study.rb', line 128
def approval
@approval
end
|
#run_count ⇒ Object
Returns the value of attribute run_count.
128
129
130
|
# File 'app/models/study.rb', line 128
def run_count
@run_count
end
|
#total_price ⇒ Object
Returns the value of attribute total_price.
128
129
130
|
# File 'app/models/study.rb', line 128
def total_price
@total_price
end
|
Instance Method Details
#abbreviation ⇒ Object
562
563
564
565
|
# File 'app/models/study.rb', line 562
def abbreviation
abbreviation = study_metadata.study_name_abbreviation
abbreviation.presence || "#{id}STDY"
end
|
#accession_all_samples ⇒ Object
558
559
560
|
# File 'app/models/study.rb', line 558
def accession_all_samples
samples.find_each(&:accession) if accession_number?
end
|
#accession_number? ⇒ Boolean
554
555
556
|
# File 'app/models/study.rb', line 554
def accession_number?
ebi_accession_number.present?
end
|
#accession_service ⇒ Object
#approved? ⇒ Boolean
571
572
573
574
|
# File 'app/models/study.rb', line 571
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.
492
493
494
495
496
|
# File 'app/models/study.rb', line 492
def asset_progress(assets = nil)
wheres = {}
wheres = { asset_id: assets.map(&:id) } if assets.present?
yield(initial_requests.asset_statistics(wheres))
end
|
#completed ⇒ Object
477
478
479
480
481
482
483
|
# File 'app/models/study.rb', line 477
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
546
547
548
|
# File 'app/models/study.rb', line 546
def dac_accession_number
study_metadata.ega_dac_accession_number
end
|
#dac_refname ⇒ Object
515
516
517
|
# File 'app/models/study.rb', line 515
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.
616
617
618
619
620
621
622
623
|
# File 'app/models/study.rb', line 616
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
567
568
569
|
# File 'app/models/study.rb', line 567
def dehumanise_abbreviated_name
abbreviation.downcase.gsub(/ +/, '_')
end
|
#each_well_for_qc_report_in_batches(exclude_existing, product_criteria, plate_purposes = nil) ⇒ Object
442
443
444
445
446
447
448
449
450
451
452
453
454
|
# File 'app/models/study.rb', line 442
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
542
543
544
|
# File 'app/models/study.rb', line 542
def ebi_accession_number
study_metadata.study_ebi_accession_number
end
|
#ethical_approval_required? ⇒ Boolean
576
577
578
579
|
# File 'app/models/study.rb', line 576
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
538
539
540
|
# File 'app/models/study.rb', line 538
def locale
funding_source
end
|
#mailing_list_of_managers ⇒ Object
600
601
602
603
|
# File 'app/models/study.rb', line 600
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
469
470
471
|
# File 'app/models/study.rb', line 469
def mark_active
logger.warn "Study activation failed! #{errors.map(&:to_s)}" unless active?
end
|
#mark_deactive ⇒ Object
465
466
467
|
# File 'app/models/study.rb', line 465
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.
534
535
536
|
# File 'app/models/study.rb', line 534
def owner
owners.first
end
|
#policy_accession_number ⇒ Object
550
551
552
|
# File 'app/models/study.rb', line 550
def policy_accession_number
study_metadata.ega_policy_accession_number
end
|
#rebroadcast ⇒ Object
609
610
611
|
# File 'app/models/study.rb', line 609
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.
487
488
489
|
# File 'app/models/study.rb', line 487
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.
499
500
501
502
503
504
505
506
507
508
509
|
# File 'app/models/study.rb', line 499
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
592
593
594
|
# File 'app/models/study.rb', line 592
def send_samples_to_service?
accession_service.no_study_accession_needed || (!study_metadata.never_release? && accession_number?)
end
|
#study ⇒ Object
525
526
527
|
# File 'app/models/study.rb', line 525
def study
self
end
|
#study_status ⇒ Object
511
512
513
|
# File 'app/models/study.rb', line 511
def study_status
inactive? ? 'closed' : 'open'
end
|
#subject_type ⇒ Object
605
606
607
|
# File 'app/models/study.rb', line 605
def subject_type
'study'
end
|
473
474
475
|
# File 'app/models/study.rb', line 473
def
.each_with_object([]) { |c, array| array << c.description if c.description.present? }.join(', ')
end
|
#unprocessed_submissions? ⇒ Boolean
519
520
521
522
|
# File 'app/models/study.rb', line 519
def unprocessed_submissions?
study.orders.any? { |o| o.submission.nil? || o.submission.unprocessed? }
end
|
#validate_ena_required_fields! ⇒ Object
596
597
598
|
# File 'app/models/study.rb', line 596
def validate_ena_required_fields!
valid?(:accession) or raise ActiveRecord::RecordInvalid, self
end
|
#validate_ethically_approved ⇒ Object
429
430
431
432
433
434
435
436
437
438
439
440
|
# File 'app/models/study.rb', line 429
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
456
457
458
459
460
461
462
463
|
# File 'app/models/study.rb', line 456
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
|