From c27ea7802289c1e7667bd27473703f2558bcb2ee Mon Sep 17 00:00:00 2001 From: Marina Gourtovaia Date: Wed, 4 Nov 2020 12:32:57 +0000 Subject: [PATCH 1/3] mqc skipper script extension for a different study --- Build.PL | 4 +- Changes | 5 + MANIFEST | 1 + bin/npg_mqc_skipper | 72 +++++- lib/npg_qc/Schema/Mqc/OutcomeDict.pm | 16 +- lib/npg_qc/mqc/skipper.pm | 359 +++++++++++++++++++++++++++ 6 files changed, 444 insertions(+), 13 deletions(-) create mode 100644 lib/npg_qc/mqc/skipper.pm diff --git a/Build.PL b/Build.PL index bc291fce4..2f8951afb 100644 --- a/Build.PL +++ b/Build.PL @@ -42,6 +42,7 @@ my $requires = { 'lib' => 0, 'List::MoreUtils' => 0, 'List::Util' => 0, + 'Log::Log4perl' => 0, 'LWP::UserAgent' => 0, 'Math::Round' => 0, 'MIME::Base64' => 0, @@ -77,6 +78,8 @@ my $requires = { 'Vcf' => 0, 'WTSI::DNAP::Utilities::Timestamp' => 0, + 'WTSI::DNAP::Warehouse::Schema' => 0, + 'WTSI::DNAP::Warehouse::Schema::Query::IseqFlowcell' => 0, 'npg_common::Alignment' => 0, 'npg_common::extractor::fastq' => 0, 'npg_common::roles::software_location' => 0, @@ -105,7 +108,6 @@ my $requires = { 'npg::util' => 0, 'st::api::base' => 0, 'st::api::lims' => 0, - 'WTSI::DNAP::Warehouse::Schema' => 0, 'WTSI::NPG::iRODS' => 0, 'WTSI::NPG::iRODS::DataObject' => 0 }; diff --git a/Changes b/Changes index 80a63e5fd..d4997364f 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,10 @@ LIST OF CHANGES FOR NPG-QC PACKAGE + - an extension for the npg_mqc_skipper script to make it work for the + Heron study, ie to take into consideration provisional manual QC + outcomes from the review autoqc resuls and finalising QC outcomes + for sequencing and libraries for runs that are expedites to archival + release 69.0.1 - npg_qc::autoqc::checks::generic::artic - bug fix in a parser for artic QC summary for cases when the summary is empty diff --git a/MANIFEST b/MANIFEST index 803b7ec2e..a407f1c43 100644 --- a/MANIFEST +++ b/MANIFEST @@ -104,6 +104,7 @@ lib/npg_qc/autoqc/role/upstream_tags.pm lib/npg_qc/autoqc/role/verify_bam_id.pm lib/npg_qc/autoqc/types.pm lib/npg_qc/mqc/reporter.pm +lib/npg_qc/mqc/skipper.pm lib/npg_qc/mqc/outcomes.pm lib/npg_qc/mqc/outcomes/keys.pm lib/npg_qc/report/genotype_call.pm diff --git a/bin/npg_mqc_skipper b/bin/npg_mqc_skipper index dc1d13c9e..d2d9faa1c 100755 --- a/bin/npg_mqc_skipper +++ b/bin/npg_mqc_skipper @@ -10,7 +10,7 @@ use Log::Log4perl qw(:levels); use DBI; use Readonly; use Try::Tiny; -use Carp; +use Class::Load qw(load_class); use npg_tracking::Schema; use WTSI::DNAP::Warehouse::Schema; @@ -23,12 +23,17 @@ Readonly::Scalar my $RUN_STATUS_FROM => 'qc review pending'; Readonly::Scalar my $RUN_STATUS_TO => 'archival pending'; Readonly::Scalar my $INSTRUMENT_MODEL => 'NovaSeq'; Readonly::Scalar my $DEPLEXING_PERCENT_THRESHOLD => 93; +Readonly::Scalar my $QC_FAILS_PERCENT_THRESHOLD => 10; my $dry_run = 1; my $study_name; +my $deplexing_threshold = $DEPLEXING_PERCENT_THRESHOLD; +my $qc_fails_threshold = $QC_FAILS_PERCENT_THRESHOLD; GetOptions('dry_run|dry-run!' => \$dry_run, 'study_name=s' => \$study_name, + 'deplexing_percent_threshold=i' => \$deplexing_threshold, + 'qc_fails_percent_threshold=i' => \$qc_fails_threshold, 'help' => sub { pod2usage(-verbose => 2, -exitval => 0) @@ -44,6 +49,10 @@ my $logger = Log::Log4perl->get_logger(); if (not $study_name) { $logger->fatal('Study name should be given, use --study_name option'); exit 1; +} else { + $logger->info("Study name: $study_name"); + $logger->info("Deplexing percent threshold: $deplexing_threshold"); + $dry_run and $logger->info('DRY RUN'); } my $tracking_schema = npg_tracking::Schema->connect(); @@ -75,7 +84,8 @@ my $get_run_ids = sub { my @run_ids = map { $_->id_run } @rows; my $placeholders = $run_list->(@run_ids); -my $dbh = WTSI::DNAP::Warehouse::Schema->connect->storage->dbh; +my $mlwh_schema = WTSI::DNAP::Warehouse::Schema->connect(); +my $dbh = $mlwh_schema->storage->dbh; my $query = q[select p.id_run, count(distinct s.id_study_lims) as study_count ] . q[from iseq_product_metrics p ] . @@ -83,7 +93,8 @@ my $query = q[join study s on s.id_study_tmp=f.id_study_tmp ] . qq[where s.name != ? and p.id_run in (${placeholders}) ] . q[group by p.id_run having study_count = ?]; -my $sth = $dbh->prepare($query) or croak "Failed to prepare statement: $DBI::errstr"; +my $sth = $dbh->prepare($query) or + ($logger->fatal("Failed to prepare statement: $DBI::errstr") and exit 1); # Run time database errors are thrown by the execute method, no need to # do anything special. $sth->execute($EXCLUDE_STUDY_NAME, @run_ids, $NUM_DISTINCT_STUDIES); @@ -96,7 +107,8 @@ if (@run_ids) { q[on p.id_iseq_flowcell_tmp=f.id_iseq_flowcell_tmp ] . q[join study s on s.id_study_tmp=f.id_study_tmp ] . qq[where s.name = ? and p.id_run in (${placeholders})]; - $sth = $dbh->prepare($query) or croak "Failed to prepare statement: $DBI::errstr"; + $sth = $dbh->prepare($query) or + ($logger->fatal("Failed to prepare statement: $DBI::errstr") and exit 1); $sth->execute($study_name, @run_ids); @run_ids = $get_run_ids->($sth); } @@ -106,8 +118,9 @@ if (@run_ids) { $query = q[select distinct(id_run) from iseq_run_lane_metrics ] . q[where (tags_decode_percent is null or tags_decode_percent < ?) ] . qq[and id_run in (${placeholders})]; - $sth = $dbh->prepare($query) or croak "Failed to prepare statement: $DBI::errstr"; - $sth->execute($DEPLEXING_PERCENT_THRESHOLD, @run_ids); + $sth = $dbh->prepare($query) or + ($logger->fatal("Failed to prepare statement: $DBI::errstr") and exit 1); + $sth->execute($deplexing_threshold, @run_ids); my $temp = {}; while (my @data = $sth->fetchrow_array()) { $temp->{$data[0]} = 1; @@ -117,6 +130,28 @@ if (@run_ids) { } } +if (@run_ids and ($study_name =~ /Heron/xms)) { + $logger->info("QC fails percent threshold: $qc_fails_threshold"); + $logger->info('Candidate runs: ' . join q[, ], @run_ids); + load_class 'npg_qc::mqc::skipper'; + load_class 'npg_qc::Schema'; + my $skipper = npg_qc::mqc::skipper->new( + npg_tracking_schema => $tracking_schema, + mlwh_schema => $mlwh_schema, + qc_schema => npg_qc::Schema->connect(), + qc_fails_threshold => $qc_fails_threshold, + id_runs => \@run_ids, + logger => $logger); + @run_ids = $skipper->select_runs(); + $logger->info('Candidate runs that satisfy QC criteria: ' . join q[, ], @run_ids); + if (!$dry_run && @run_ids) { + @run_ids = $skipper->save_review_results(@run_ids); + } + if (!$dry_run && @run_ids) { + @run_ids = $skipper->set_final_seq_outcomes(@run_ids); + } +} + if (@run_ids) { $logger->info(join qq[\n\t], q[], qq[Study '$study_name'], @@ -201,12 +236,19 @@ npg_mqc_skipper =item --dry_run or --dry-run and --no-dry_run and --no-dry-run +=item --deplexing_percent_threshold + +=item --qc_fails_percent_threshold + +=item --help + =back =head1 EXIT STATUS -0 if all runs were processed succesfully, 1 if there are problems while Updating -any record and the transaction fails to rollback +0 if all runs were processed succesfully, 1 if there are problems +getting an initial list of runs or while updating any record and the +transaction fails to rollback. =head1 CONFIGURATION @@ -226,6 +268,14 @@ In non-dry run mode the status of this runs is changed to 'archival pending', ie the manual QC stage of the run life cycle is skipped. +Additionally, for studies containing string 'Heron', before the run +status is changed, an assessment of automatic Robo QC results is +performed to decide whether the run can be expedited to archival. If +a run can be moved to archival, preliminary autoqc review results are +saved to a database, which saves preliminary QC outcomes to the +database. Following this, sequencing and library QC outcomes are +finalised for all lanes of this run. + =head1 SUBROUTINES/METHODS =head1 DIAGNOSTICS @@ -244,8 +294,6 @@ is skipped. =item FindBin -=item Carp - =item Log::Log4perl =item Getopt::Long @@ -258,6 +306,8 @@ is skipped. =item Try::Tiny +=item Class::Load + =item npg_tracking::Schema =item WTSI::DNAP::Warehouse::Schema @@ -274,7 +324,7 @@ Marina Gourtovaia =head1 LICENSE AND COPYRIGHT -Copyright (C) 2019 Genome Research Limited +Copyright (C) 2019,2020 Genome Research Ltd. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/lib/npg_qc/Schema/Mqc/OutcomeDict.pm b/lib/npg_qc/Schema/Mqc/OutcomeDict.pm index f56572186..7bd08a2af 100644 --- a/lib/npg_qc/Schema/Mqc/OutcomeDict.pm +++ b/lib/npg_qc/Schema/Mqc/OutcomeDict.pm @@ -17,6 +17,11 @@ sub is_final_outcome_description { return $desc =~ /$FINAL\Z/smx; #The short description includes the word final. } +sub is_rejected_outcome_description { + my ($self, $desc) = @_; + return $desc =~ /\A$REJECTED/smx; +} + sub is_final_outcome { my $self = shift; return $self->is_final_outcome_description($self->short_desc); @@ -29,7 +34,7 @@ sub is_accepted { sub is_rejected { my $self = shift; - return $self->short_desc =~ /\A $REJECTED/smx; #The short description includes the word rejected. + return $self->is_rejected_outcome_description($self->short_desc); } sub is_final_accepted { @@ -114,6 +119,15 @@ __END__ __PACKAGE__->is_final_outcome_description('Accepted final'); # returns true $row->is_final_outcome_description('Accepted preliminary'); # returns false +=head2 is_rejected_outcome_description + + Argument - short qc outcome description. + Returns true if the argument describes a rejected outcome, false otherwise. + Can be used as both class and instance method. + + __PACKAGE__->is_rejected_outcome_description('Rejected final'); # returns true + $row->is_rejected_outcome_description('Accepted preliminary'); # returns false + =head2 is_final_outcome Utility method to check if the outcome is considered final. diff --git a/lib/npg_qc/mqc/skipper.pm b/lib/npg_qc/mqc/skipper.pm new file mode 100644 index 000000000..63d70a8c3 --- /dev/null +++ b/lib/npg_qc/mqc/skipper.pm @@ -0,0 +1,359 @@ +package npg_qc::mqc::skipper; + +use Moose; +use MooseX::StrictConstructor; +use namespace::autoclean; +use Readonly; +use Try::Tiny; +use List::MoreUtils qw/uniq/; + +use npg_tracking::illumina::runfolder; +use WTSI::DNAP::Warehouse::Schema::Query::IseqFlowcell; +use npg_qc::autoqc::qc_store::query; +use npg_qc::autoqc::qc_store; +use npg_qc::autoqc::qc_store::options qw/ $PLEXES /; +use npg_qc::autoqc::db_loader; +use npg_qc::mqc::outcomes; +use npg_qc::mqc::outcomes::keys qw/ $SEQ_OUTCOMES /; + +our $VERSION = '0'; + +Readonly::Scalar my $HUNDRED => 100; +Readonly::Scalar my $USER_NAME => 'pipeline'; +Readonly::Scalar my $CHECK_NAME => 'review'; +Readonly::Scalar my $OUTCOME_TYPE => 'mqc_outcome'; +Readonly::Scalar my $LANE_OUTCOME => 'Accepted final'; + +has 'qc_schema' => ( + isa => 'npg_qc::Schema', + is => 'ro', + required => 1, +); + +has 'mlwh_schema' => ( + isa => 'WTSI::DNAP::Warehouse::Schema', + is => 'ro', + required => 1, +); + +has 'npg_tracking_schema' => ( + isa => 'npg_tracking::Schema', + is => 'ro', + required => 1, +); + +has 'qc_fails_threshold' => ( + isa => 'Num', + is => 'ro', + required => 1, +); + +has 'id_runs' => ( + isa => 'ArrayRef', + is => 'ro', + required => 1, +); + +has 'logger' => ( + isa => 'Log::Log4perl::Logger', + is => 'ro', + required => 1, +); + +sub select_runs { + my $self = shift; + + my @ids = (); + foreach my $id_run (@{$self->id_runs}) { + my $skip = 0; + try { + $skip = $self->_can_skip_mqc4run($id_run); + } catch { + $self->logger->error($_); + }; + $skip and push @ids, $id_run; + } + + return @ids; +} + +sub save_review_results { + my ($self, @id_runs) = @_; + + my @ids = (); + foreach my $id_run (@id_runs) { + my $num_loaded = 0; + try { + my $ap = npg_tracking::illumina::runfolder->new( + id_run => $id_run, + npg_tracking_schema => $self->npg_tracking_schema + )->archive_path; + my $loader = npg_qc::autoqc::db_loader->new( + schema => $self->qc_schema, + check => [$CHECK_NAME], + id_run => $id_run, + archive_path => $ap, + verbose => 0 + ); + $num_loaded = $loader->load(); + } catch { + $self->logger->error($_); + }; + $num_loaded and push @ids, $id_run; + } + + return @ids; +} + +sub set_final_seq_outcomes { + my ($self, @id_runs) = @_; + + my @ids = (); + my $o = npg_qc::mqc::outcomes->new(qc_schema => $self->qc_schema); + + foreach my $id_run (@id_runs) { + my @info = values %{$self->_sample_info->{$id_run}}; + my @positions = sort { $a <=> $b } + uniq + map { $_->{position} } + @info; + my $outcomes = {}; + my $tag_info = {}; + try { + for my $position (@positions) { + my $key = join q[:], $id_run, $position; + $outcomes->{$SEQ_OUTCOMES}->{$key} = {$OUTCOME_TYPE => $LANE_OUTCOME}; + my @tags = sort { $a <=> $b } + map { $_->{tag_index} } + grep { $_->{position} == $position } + @info; + $tag_info->{$key} = \@tags; + } + $o->save($outcomes, $USER_NAME, $tag_info); + push @ids, $id_run; + } catch { + $self->logger->error($_); + }; + } + + return @ids; +} + +has '_sample_info' => ( + isa => 'HashRef', + is => 'ro', + required => 0, + lazy_build => 1, +); +sub _build__sample_info { + my $self = shift; + + my $entity_type = $WTSI::DNAP::Warehouse::Schema::Query::IseqFlowcell::INDEXED_LIBRARY; + my $rs = $self->mlwh_schema->resultset('IseqProductMetric') + ->search( {'me.id_run' => $self->id_runs, + 'iseq_flowcell.entity_type' => $entity_type}, + {join => [{'iseq_flowcell' => 'sample'}]} ); + my $info = {}; + while (my $row = $rs->next) { + my $sample_type = $row->iseq_flowcell->sample->control_type; + $sample_type ||= q[]; + $info->{$row->id_run}->{$row->id_iseq_product} = + {sample_type => $sample_type, + position => $row->position, + tag_index => $row->tag_index}; + } + + return $info; +} + +sub _can_skip_mqc4run { + my ($self, $id_run) = @_; + + my $sample_info = $self->_sample_info->{$id_run}; + my $query = npg_qc::autoqc::qc_store::query->new( + id_run => $id_run, + db_qcresults_lookup => 0, + option => $PLEXES, + npg_tracking_schema => $self->npg_tracking_schema); + my $collection = npg_qc::autoqc::qc_store->new( + use_db => 0, + checks_list => [$CHECK_NAME])->load_from_staging($query); + my $num_results = $collection->size; + if ($num_results == 0) { + $self->logger->warn("No autoqc review results retrieved for run $id_run"); + return; + } + my $num_expected = scalar keys %{$sample_info}; + if ($num_results != $num_expected) { + $self->logger->error( + "Number of autoqc review results retrieved for run ${id_run}: " . + "expected - $num_expected, actual - $num_results"); + return; + } else { + $self->logger->info( + "$num_results autoqc review results retrieved for run ${id_run}"); + } + + my @controls = (); + my $real_samples = {}; + foreach my $r ($collection->all) { + # We need outcomes to be defined + ($r->qc_outcome and $r->qc_outcome->{mqc_outcome}) or return; + my $d = $r->composition->digest; + if (not exists $sample_info->{$d}) { + $self->logger->error('No sample info for ' . $r->composition->freeze); + return; + } + if ($sample_info->{$d}->{sample_type}) { + push @controls, $r; + } else { + push @{$real_samples->{$sample_info->{$d}->{position}}}, $r; + } + } + + (@controls and keys %{$real_samples}) or return; + + my $has_failed = sub { + my $r = shift; + return npg_qc::Schema::Mqc::OutcomeDict + ->is_rejected_outcome_description($r->qc_outcome->{mqc_outcome}); + }; + + # Not fast-tracking if one or more positive or negative controls + # fails across all lanes of a run. + my @failed = grep { $has_failed->($_) } @controls; + if (@failed) { + $self->logger->info("At least one of the controls failed in run $id_run"); + return; + } + + # Not fast-tracking if the threshold of failed samples is exceeded + # in one of the lanes. + foreach my $lane (keys %{$real_samples}) { + my $samples = $real_samples->{$lane}; + @failed = grep { $has_failed->($_) } @{$samples}; + my $num_total = @{$samples}; + my $num_failed = @failed; + $self->logger->info(sprintf 'Run %i lane %i: failed %i real samples out of %i', + $id_run, $lane, $num_failed, $num_total); + ( ($num_failed/$num_total) * $HUNDRED <= $self->qc_fails_threshold ) or return; + } + + return 1; # Got to the end - fine to fast-track. +} + +__PACKAGE__->meta->make_immutable; + +1; + +__END__ + +=head1 NAME + +npg_qc::mqc::skipper + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +Helper class for the npg_mqc_skipper script. + +=head1 SUBROUTINES/METHODS + +=head2 qc_schema + +DBIx schema object for the QC database, required attribute. + +=head2 mlwh_schema + +DBIx schema object for the mlwh database, required attribute. + +=head2 npg_tracking_schema + +DBIx schema object for the tracking database, required attribute. + +=head2 qc_fails_threshold + +QC fails threshold, percents, required attribute. + +=head2 id_runs + +An original array of run ids to consider, required attribute. + +=head2 logger + +Log::Log4perl::Logger logger object, required attribute. + +=head2 select_runs + +Returns a subset list of run ids for runs, which can be advanced to the +archival stage bypassing the manual QC process. + +=head2 save_review_results + +Given a list of run ids, saves review results for this runs to the QC +database and returns a subset list of run ids, for which this operation was +successful. + +=head2 set_final_seq_outcomes + +Given a list of run ids, creates and saves final sequencing QC outcomes for +each lane of the given runs and returns a subset list of run ids, for which +this operation was successful. Saving a final sequencing QC outcome for a lane +automatically finalises preliminary library QC outcomes for all products in +the pool. + +=head1 DIAGNOSTICS + +=head1 CONFIGURATION AND ENVIRONMENT + +=head1 DEPENDENCIES + +=over + +=item Moose + +=item MooseX::StrictConstructor + +=item namespace::autoclean + +=item Readonly + +=item Try::Tiny + +=item List::MoreUtils + +=item npg_tracking::illumina::runfolder + +=item WTSI::DNAP::Warehouse::Schema::Query::IseqFlowcell + +=back + +=head1 INCOMPATIBILITIES + +=head1 BUGS AND LIMITATIONS + +=head1 AUTHOR + +Marina Gourtovaia Emg8@sanger.ac.ukE + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2020 GRL + +This file is part of NPG. + +NPG is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +=cut From 8be9dabb8187a03a8e61d20e1427fadf155df60f Mon Sep 17 00:00:00 2001 From: Marina Gourtovaia Date: Mon, 23 Nov 2020 12:32:35 +0000 Subject: [PATCH 2/3] avoid globbing for autoqc results twice; add a simple test --- MANIFEST | 1 + lib/npg_qc/mqc/skipper.pm | 48 +++++++++++++++++++-------------- t/70-mqc-skipper.t | 56 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 t/70-mqc-skipper.t diff --git a/MANIFEST b/MANIFEST index a407f1c43..19a3c704c 100644 --- a/MANIFEST +++ b/MANIFEST @@ -270,6 +270,7 @@ t/60-autoqc-results-tag_metrics.t t/60-autoqc-results-verify_bam_id.t t/70-mqc-reporter.t t/70-mqc-outcomes.t +t/70-mqc-skipper.t t/70-report-genotype_call.t t/80-autoqc-qc-script.t t/data/autoqc/bam_flagstats/16960_1_0.tar.gz diff --git a/lib/npg_qc/mqc/skipper.pm b/lib/npg_qc/mqc/skipper.pm index 63d70a8c3..0a39b82aa 100644 --- a/lib/npg_qc/mqc/skipper.pm +++ b/lib/npg_qc/mqc/skipper.pm @@ -7,7 +7,6 @@ use Readonly; use Try::Tiny; use List::MoreUtils qw/uniq/; -use npg_tracking::illumina::runfolder; use WTSI::DNAP::Warehouse::Schema::Query::IseqFlowcell; use npg_qc::autoqc::qc_store::query; use npg_qc::autoqc::qc_store; @@ -83,22 +82,23 @@ sub save_review_results { my @ids = (); foreach my $id_run (@id_runs) { my $num_loaded = 0; - try { - my $ap = npg_tracking::illumina::runfolder->new( - id_run => $id_run, - npg_tracking_schema => $self->npg_tracking_schema - )->archive_path; - my $loader = npg_qc::autoqc::db_loader->new( - schema => $self->qc_schema, - check => [$CHECK_NAME], - id_run => $id_run, - archive_path => $ap, - verbose => 0 - ); - $num_loaded = $loader->load(); - } catch { - $self->logger->error($_); - }; + my $paths = $self->_review_json_path->{$id_run}; + if ($paths and @{$paths}) { + try { + $num_loaded = npg_qc::autoqc::db_loader->new( + schema => $self->qc_schema, + check => [$CHECK_NAME], + id_run => $id_run, + json_file => $paths, + verbose => 0 + )->load(); + } catch { + $self->logger->error($_); + }; + } else { + $self->logger->error( + "List of paths for run $id_run is missing"); + } $num_loaded and push @ids, $id_run; } @@ -166,6 +166,13 @@ sub _build__sample_info { return $info; } +has '_review_json_path' => ( + isa => 'HashRef', + is => 'ro', + required => 0, + default => sub { return {} }, +); + sub _can_skip_mqc4run { my ($self, $id_run) = @_; @@ -196,7 +203,9 @@ sub _can_skip_mqc4run { my @controls = (); my $real_samples = {}; - foreach my $r ($collection->all) { + my @results = $collection->all; + + foreach my $r (@results) { # We need outcomes to be defined ($r->qc_outcome and $r->qc_outcome->{mqc_outcome}) or return; my $d = $r->composition->digest; @@ -239,6 +248,7 @@ sub _can_skip_mqc4run { ( ($num_failed/$num_total) * $HUNDRED <= $self->qc_fails_threshold ) or return; } + $self->_review_json_path->{$id_run} = [map { $_->result_file_path } @results]; return 1; # Got to the end - fine to fast-track. } @@ -323,8 +333,6 @@ the pool. =item List::MoreUtils -=item npg_tracking::illumina::runfolder - =item WTSI::DNAP::Warehouse::Schema::Query::IseqFlowcell =back diff --git a/t/70-mqc-skipper.t b/t/70-mqc-skipper.t new file mode 100644 index 000000000..e0bdeeb7c --- /dev/null +++ b/t/70-mqc-skipper.t @@ -0,0 +1,56 @@ +use strict; +use warnings; +use Test::More tests => 2; +use Test::Exception; +use Moose::Meta::Class; +use Log::Log4perl qw(:levels); +use npg_testing::db; + +use_ok('npg_qc::mqc::skipper'); + +sub _create_qc_schema { + return Moose::Meta::Class->create_anon_class( + roles => [qw/npg_testing::db/])->new_object()->create_test_db( + q[npg_qc::Schema], q[t/data/fixtures]); +} + +sub _create_mlwh_schema { + return Moose::Meta::Class->create_anon_class( + roles => [qw/npg_testing::db/] + )->new_object()->create_test_db(q[WTSI::DNAP::Warehouse::Schema]); +} + +sub _create_tracking_schema { + return Moose::Meta::Class->create_anon_class( + roles => [qw/npg_testing::db/] + )->new_object()->create_test_db( + q[npg_tracking::Schema], q[t/data/fixtures/npg_tracking] + ); +} + +my $qc_schema = _create_qc_schema(); +my $mlwh_schema = _create_mlwh_schema(); +my $tracking_schema = _create_tracking_schema(); + +my $layout = '%d %-5p %c - %m%n'; +Log::Log4perl->easy_init({layout => $layout, + level => $WARN, + utf8 => 1}); +my $logger = Log::Log4perl->get_logger(); + +subtest 'create object' => sub { + plan tests => 1; + + my $skipper = npg_qc::mqc::skipper->new( + qc_schema => $qc_schema, + mlwh_schema => $mlwh_schema, + npg_tracking_schema => $tracking_schema, + id_runs => [1, 2], + qc_fails_threshold => 90, + logger => $logger + ); + isa_ok($skipper, 'npg_qc::mqc::skipper'); +}; + +1; + From fd82c4bdc7ec51a482307dab7b2e61f54c9ab4e2 Mon Sep 17 00:00:00 2001 From: mgcam Date: Fri, 4 Dec 2020 11:12:21 +0000 Subject: [PATCH 3/3] Changes file update for release 69.1.0 --- Changes | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index d4997364f..6f1a29ffa 100644 --- a/Changes +++ b/Changes @@ -1,9 +1,10 @@ LIST OF CHANGES FOR NPG-QC PACKAGE +release 69.1.0 - an extension for the npg_mqc_skipper script to make it work for the Heron study, ie to take into consideration provisional manual QC - outcomes from the review autoqc resuls and finalising QC outcomes - for sequencing and libraries for runs that are expedites to archival + outcomes from the review autoqc resuls and finalise QC outcomes + for sequencing and libraries for runs that are expedited to archival release 69.0.1 - npg_qc::autoqc::checks::generic::artic - bug fix in a parser for