diff --git a/Changes b/Changes index f0a00bc97c..9bc468e2c6 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,16 @@ {{$NEXT}} +0.272 2014-12-04 07:18:46 Europe/London + +0.272 2014-12-03 16:04:43 Europe/London + + Bugfix: recovery class foreign key added to correct column + Update interface for crispr es qc, allow validation of qc run. + +0.271 2014-12-03 07:42:17 Europe/London + + Update Legacy Cre KnockIn report + 0.270 2014-11-21 15:05:18 Europe/London QcRunSummary report gene symbols and support for crispr qc diff --git a/ddl/versions/78/fixtures.sql b/ddl/versions/78/fixtures.sql new file mode 100755 index 0000000000..def5d0e27e --- /dev/null +++ b/ddl/versions/78/fixtures.sql @@ -0,0 +1 @@ +INSERT INTO schema_versions(version) VALUES (78); \ No newline at end of file diff --git a/ddl/versions/78/up.sql b/ddl/versions/78/up.sql new file mode 100755 index 0000000000..90eeb03382 --- /dev/null +++ b/ddl/versions/78/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE projects ADD CONSTRAINT projects_recovery_class_fkey FOREIGN KEY (recovery_class) REFERENCES project_recovery_class(id); +ALTER TABLE projects DROP CONSTRAINT projects_recovery_comment_fkey; \ No newline at end of file diff --git a/ddl/versions/79/audit-up.sql b/ddl/versions/79/audit-up.sql new file mode 100644 index 0000000000..691588b8d5 --- /dev/null +++ b/ddl/versions/79/audit-up.sql @@ -0,0 +1,2 @@ +ALTER TABLE audit.crispr_es_qc_wells ADD COLUMN variant_size INT; +ALTER TABLE audit.crispr_es_qc_runs ADD COLUMN validated BOOLEAN; diff --git a/ddl/versions/79/fixtures.sql b/ddl/versions/79/fixtures.sql new file mode 100644 index 0000000000..06f80e03cf --- /dev/null +++ b/ddl/versions/79/fixtures.sql @@ -0,0 +1,2 @@ +INSERT INTO schema_versions(version) VALUES (79); +INSERT INTO crispr_damage_types( id, description ) VALUES ( 'no-call', 'No automatic damage type call could be made' ); diff --git a/ddl/versions/79/up.sql b/ddl/versions/79/up.sql new file mode 100644 index 0000000000..64f7ccf9e1 --- /dev/null +++ b/ddl/versions/79/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE crispr_es_qc_wells ADD COLUMN variant_size INT; +ALTER TABLE crispr_es_qc_runs ADD COLUMN validated BOOLEAN DEFAULT FALSE; diff --git a/lib/LIMS2/Model/Plugin/CrisprEsQc.pm b/lib/LIMS2/Model/Plugin/CrisprEsQc.pm index c1d6e75d69..8ecb4dabab 100644 --- a/lib/LIMS2/Model/Plugin/CrisprEsQc.pm +++ b/lib/LIMS2/Model/Plugin/CrisprEsQc.pm @@ -4,6 +4,7 @@ use strict; use warnings FATAL => 'all'; use Moose::Role; +use DDP; use namespace::autoclean; requires qw( schema check_params throw retrieve log trace ); @@ -57,6 +58,9 @@ sub pspec_create_crispr_es_qc_well { vcf_file => { validate => 'non_empty_string', optional => 1 }, crispr_es_qc_run_id => { validate => 'non_empty_string' }, species => { validate => 'existing_species' }, + crispr_damage_type => { validate => 'existing_crispr_damage_type', optional => 1, rename => 'crispr_damage_type_id' }, + variant_size => { validate => 'integer', optional => 1 }, + accepted => { validate => 'boolean', optional => 1 }, }; } @@ -70,6 +74,19 @@ sub create_crispr_es_qc_well { my $validated_params = $self->check_params( $params, $self->pspec_create_crispr_es_qc_well ); + # if the qc well has been marked accepted run this logic + if ( $validated_params->{accepted} ) { + my $well = $self->retrieve_well( { id => $validated_params->{well_id} } ); + # only mark qc accepted if the linked well is not already accepted in another run + if ( $well->accepted ) { + delete $validated_params->{accepted}; + } + # mark the linked well accepted + else { + $well->update( { accepted => 1 } ); + } + } + my $species = delete $validated_params->{species}; if ( $validated_params->{crispr_chr_name} ) { my $chr_name = delete $validated_params->{crispr_chr_name}; @@ -154,10 +171,33 @@ sub retrieve_crispr_es_qc_run { return $self->retrieve( CrisprEsQcRuns => $validated_params ); } -sub pspec_update_crispr_well_damage { +=head2 validate_crispr_es_qc_run + +Update a crispr es qc run plus all of its wells to validated. + +=cut +sub validate_crispr_es_qc_run { + my ( $self, $params ) = @_; + + my $crispr_es_qc_run = $self->retrieve_crispr_es_qc_run( $params ); + $crispr_es_qc_run->update( { validated => 1 } ); + + $self->log->info( 'Validated crispr es qc run ' . $crispr_es_qc_run->id ); + + return; +} + +sub pspec_update_crispr_es_qc_well { return { id => { validate => 'integer' }, - damage_type => { validate => 'existing_crispr_damage_type', optional => 1 }, + damage_type => { + validate => 'existing_crispr_damage_type', + optional => 1, + rename => 'crispr_damage_type_id' + }, + variant_size => { validate => 'integer', optional => 1 }, + accepted => { validate => 'boolean_string', optional => 1 }, + MISSING_OPTIONAL_VALID => 1, }; } @@ -166,28 +206,28 @@ sub pspec_update_crispr_well_damage { Update the crispr_damage type value of a crispr es qc well row =cut -sub update_crispr_well_damage { +sub update_crispr_es_qc_well{ my ( $self, $params ) = @_; - # to deal with user unsetting a damage type - delete $params->{damage_type} unless $params->{damage_type}; - - my $validated_params = $self->check_params( $params, $self->pspec_update_crispr_well_damage ); + my $validated_params = $self->check_params( $params, $self->pspec_update_crispr_es_qc_well ); - my $qc_well = $self->schema->resultset('CrisprEsQcWell')->find( - { - id => $validated_params->{id}, - }, - ); + my $id = delete $validated_params->{id}; + my $qc_well = $self->schema->resultset('CrisprEsQcWell')->find( { id => $id } ); unless ( $qc_well ) { $self->throw( - NotFound => { entity_class => 'CrisprEsQcWell', search_params => $validated_params } - ); + NotFound => { entity_class => 'CrisprEsQcWell', search_params => { id => $id } } ); } - my $damage_type = $validated_params->{damage_type} ? $validated_params->{damage_type} : undef; - $qc_well->update( { crispr_damage_type_id => $damage_type } ); - $self->log->info( "Updated crispr damage type on crispr es qc well: " . $qc_well->id . ' to: ' . ( $damage_type ? $damage_type : 'unset' ) ); + $qc_well->update( $validated_params ); + + $self->log->info( "Updated crispr es qc well " + . $qc_well->id . ' to: ' . p( $validated_params ) ); + + if ( exists $validated_params->{accepted} ) { + my $well = $qc_well->well; + $well->update( { accepted => $validated_params->{accepted} } ); + $self->log->info( "Updated $well well accepted " . $validated_params->{accepted} ); + } return $qc_well; } diff --git a/lib/LIMS2/Model/Schema/Result/CrisprEsQcRuns.pm b/lib/LIMS2/Model/Schema/Result/CrisprEsQcRuns.pm index 3eb8accf1b..1ed3238cf2 100644 --- a/lib/LIMS2/Model/Schema/Result/CrisprEsQcRuns.pm +++ b/lib/LIMS2/Model/Schema/Result/CrisprEsQcRuns.pm @@ -73,6 +73,12 @@ __PACKAGE__->table("crispr_es_qc_runs"); data_type: 'text' is_nullable: 1 +=head2 validated + + data_type: 'boolean' + default_value: false + is_nullable: 1 + =cut __PACKAGE__->add_columns( @@ -93,6 +99,8 @@ __PACKAGE__->add_columns( { data_type => "text", is_foreign_key => 1, is_nullable => 0 }, "sub_project", { data_type => "text", is_nullable => 1 }, + "validated", + { data_type => "boolean", default_value => \"false", is_nullable => 1 }, ); =head1 PRIMARY KEY @@ -155,8 +163,8 @@ __PACKAGE__->belongs_to( ); -# Created by DBIx::Class::Schema::Loader v0.07022 @ 2014-04-16 16:43:15 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:KStXhk631rbpalREDS3uoA +# Created by DBIx::Class::Schema::Loader v0.07022 @ 2014-12-01 11:39:17 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:A7ZSinww6G/DqZs50j+0+w sub as_hash { my ( $self, $options ) = @_; diff --git a/lib/LIMS2/Model/Schema/Result/CrisprEsQcWell.pm b/lib/LIMS2/Model/Schema/Result/CrisprEsQcWell.pm index 2e0d623033..450948a915 100644 --- a/lib/LIMS2/Model/Schema/Result/CrisprEsQcWell.pm +++ b/lib/LIMS2/Model/Schema/Result/CrisprEsQcWell.pm @@ -111,6 +111,11 @@ __PACKAGE__->table("crispr_es_qc_wells"); is_foreign_key: 1 is_nullable: 1 +=head2 variant_size + + data_type: 'integer' + is_nullable: 1 + =cut __PACKAGE__->add_columns( @@ -145,6 +150,8 @@ __PACKAGE__->add_columns( { data_type => "text", is_nullable => 1 }, "crispr_damage_type_id", { data_type => "text", is_foreign_key => 1, is_nullable => 1 }, + "variant_size", + { data_type => "integer", is_nullable => 1 }, ); =head1 PRIMARY KEY @@ -232,8 +239,8 @@ __PACKAGE__->belongs_to( ); -# Created by DBIx::Class::Schema::Loader v0.07022 @ 2014-10-27 09:07:15 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZqMaBc51jDRz1dseBj+8Ig +# Created by DBIx::Class::Schema::Loader v0.07022 @ 2014-12-03 11:52:40 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:HLxWkhqfpzrHco4xOT7tdQ use JSON; use List::Util qw ( min max ); @@ -367,6 +374,7 @@ sub format_well_data { rev_read => $self->rev_read, damage_type => $self->crispr_damage_type_id, vep_output => $json->{vep_output}, + variant_size => $self->variant_size, }; } diff --git a/lib/LIMS2/Model/Schema/Result/Project.pm b/lib/LIMS2/Model/Schema/Result/Project.pm index fcd3867e19..d75101ef35 100644 --- a/lib/LIMS2/Model/Schema/Result/Project.pm +++ b/lib/LIMS2/Model/Schema/Result/Project.pm @@ -86,12 +86,12 @@ __PACKAGE__->table("projects"); =head2 recovery_class data_type: 'text' + is_foreign_key: 1 is_nullable: 1 =head2 recovery_comment data_type: 'text' - is_foreign_key: 1 is_nullable: 1 =head2 priority @@ -124,9 +124,9 @@ __PACKAGE__->add_columns( "effort_concluded", { data_type => "boolean", default_value => \"false", is_nullable => 0 }, "recovery_class", - { data_type => "text", is_nullable => 1 }, - "recovery_comment", { data_type => "text", is_foreign_key => 1, is_nullable => 1 }, + "recovery_comment", + { data_type => "text", is_nullable => 1 }, "priority", { data_type => "text", is_nullable => 1 }, ); @@ -183,7 +183,7 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 recovery_comment +=head2 recovery_class Type: belongs_to @@ -192,9 +192,9 @@ Related object: L =cut __PACKAGE__->belongs_to( - "recovery_comment", + "recovery_class", "LIMS2::Model::Schema::Result::ProjectRecoveryClass", - { id => "recovery_comment" }, + { id => "recovery_class" }, { is_deferrable => 1, join_type => "LEFT", @@ -218,8 +218,9 @@ __PACKAGE__->belongs_to( { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, ); -# Created by DBIx::Class::Schema::Loader v0.07022 @ 2014-10-14 11:17:00 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:djydxXiT/U9oK3BE/QW9iw + +# Created by DBIx::Class::Schema::Loader v0.07022 @ 2014-12-03 15:40:30 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:gp9bd7euuZta5rwWDBvWUQ sub as_hash { my $self = shift; diff --git a/lib/LIMS2/Model/Schema/Result/ProjectRecoveryClass.pm b/lib/LIMS2/Model/Schema/Result/ProjectRecoveryClass.pm index 24052f35b9..f89db0e527 100644 --- a/lib/LIMS2/Model/Schema/Result/ProjectRecoveryClass.pm +++ b/lib/LIMS2/Model/Schema/Result/ProjectRecoveryClass.pm @@ -83,12 +83,13 @@ Related object: L __PACKAGE__->has_many( "projects", "LIMS2::Model::Schema::Result::Project", - { "foreign.recovery_comment" => "self.id" }, + { "foreign.recovery_class" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, ); -# Created by DBIx::Class::Schema::Loader v0.07022 @ 2014-10-14 14:05:24 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SANap7GZzTCDqebVX8hgAw + +# Created by DBIx::Class::Schema::Loader v0.07022 @ 2014-12-03 15:40:30 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:WWpruQh1u9fbedff6V50mg # You can replace this text with custom code or comments, and it will be preserved on regeneration __PACKAGE__->meta->make_immutable; diff --git a/lib/LIMS2/Model/Schema/Result/QcEngSeq.pm b/lib/LIMS2/Model/Schema/Result/QcEngSeq.pm index 5ecf9c1903..380091c431 100644 --- a/lib/LIMS2/Model/Schema/Result/QcEngSeq.pm +++ b/lib/LIMS2/Model/Schema/Result/QcEngSeq.pm @@ -170,5 +170,13 @@ sub design_id { return $eng_seq_params->{design_id}; } +sub crispr_id { + my $self = shift; + + my $eng_seq_params = decode_json( $self->params ); + + return $eng_seq_params->{crispr_id}; +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/lib/LIMS2/Model/Schema/Result/QcTemplateWell.pm b/lib/LIMS2/Model/Schema/Result/QcTemplateWell.pm index 4f4b3d0b42..efc9cd1cf1 100644 --- a/lib/LIMS2/Model/Schema/Result/QcTemplateWell.pm +++ b/lib/LIMS2/Model/Schema/Result/QcTemplateWell.pm @@ -250,5 +250,11 @@ sub design_id { return $self->qc_eng_seq->design_id; } +sub crispr_id { + my $self = shift; + + return $self->qc_eng_seq->crispr_id; +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/lib/LIMS2/Model/Util/CrisprESQC.pm b/lib/LIMS2/Model/Util/CrisprESQC.pm index 71b2037df8..5a797a0910 100644 --- a/lib/LIMS2/Model/Util/CrisprESQC.pm +++ b/lib/LIMS2/Model/Util/CrisprESQC.pm @@ -694,8 +694,14 @@ sub build_qc_data { $qc_data{crispr_chr_name} = $crispr->chr_name; } - if ( $analyser && $analyser->vcf_file_target_region ) { - $qc_data{vcf_file} = $analyser->vcf_file_target_region->slurp; + if ( $analyser ) { + $qc_data{vcf_file} = $analyser->vcf_file_target_region->slurp if $analyser->vcf_file_target_region; + $qc_data{variant_size} = $analyser->variant_size if $analyser->variant_size; + if ( $analyser->variant_type ) { + $qc_data{crispr_damage_type} = $analyser->variant_type; + # if a variant type other can no-call has been made then mark well accepted + $qc_data{accepted} = 1 if $analyser->variant_type ne 'no-call'; + } } if ( exists $well_reads->{forward} ) { diff --git a/lib/LIMS2/Model/Util/LegacyCreKnockInProjectReport.pm b/lib/LIMS2/Model/Util/LegacyCreKnockInProjectReport.pm index 81af35ea01..f0371592d7 100644 --- a/lib/LIMS2/Model/Util/LegacyCreKnockInProjectReport.pm +++ b/lib/LIMS2/Model/Util/LegacyCreKnockInProjectReport.pm @@ -16,7 +16,7 @@ Cre KnockIn projects. use Moose; use Try::Tiny; use LIMS2::Util::Tarmits; -use List::MoreUtils qw( any part ); +use List::MoreUtils qw( any part uniq ); use namespace::autoclean; with qw( MooseX::Log::Log4perl ); @@ -44,7 +44,7 @@ has projects => ( ); has project_id => ( - is => 'ro', + is => 'ro', isa => 'Int', ); @@ -76,7 +76,7 @@ has project_well_summary_data => ( lazy_build => 1, handles => { have_project_data => 'exists', - get_project_data => 'get', + get_project_data => 'get', } ); @@ -119,18 +119,35 @@ sub generate_report_data { }; $marker_symbol = $gene ? $gene->{gene_symbol} : undef; } - my $htgt_project_id = $project->htgt_project_id ? $project->htgt_project_id : ''; - my $project_status - = $project_data ? $self->find_project_status( $project, $project_data ) : ''; + my $project_status = ''; + my $allele_details = {}; + if ( $project_data ) { + my @es_cell_names = map { $_->{clone_plate_name} . '_' . $_->{clone_well_name} } + grep { $_->{clone_well_id} } @{ $project_data->{ws} }; + my @emi_attempts = @{ $self->emi_attempts( \@es_cell_names ) }; + + $project_status = $self->find_project_status( $project, $project_data, \@emi_attempts ); + + # Can only get allele details for projects with the following statuses + # Mice - genotype confirmed + # Mice - Microinjection in progress + # ES Cells - Targeting Confirmed + if ( $project_status =~ /^Mice|Targeting Confirmed/ ) { + $allele_details = $self->find_allele_names( \@es_cell_names, \@emi_attempts ); + } + } $self->log->info( "Status: $project_status" ); push @report_data, [ - $htgt_project_id, - $project->id, $marker_symbol, $project->gene_id, $project_status, + join( ' | ', keys %{ $allele_details } ), + join( ' | ', values %{ $allele_details } ), + scalar( keys %{ $allele_details } ), + $project_data->{cassette}, + $project_data->{design_id}, ] }; Log::Log4perl::NDC->remove; @@ -146,21 +163,19 @@ work out status of project =cut sub find_project_status { - my ( $self, $project, $project_data ) = @_; + my ( $self, $project, $project_data, $emi_attempts ) = @_; my $ws_rows = $project_data->{ws}; $self->log->debug( 'Project has ' . scalar(@{ $ws_rows }) . ' summary row(s)' ); - my @dist_es_cells = @{ $self->distributable_es_cells( $ws_rows ) }; - - my @emi_attempts = @{ $self->emi_attempts( $ws_rows ) }; + my @dist_es_cells = @{ $self->distributable_es_cells( $ws_rows ) }; $self->log->debug('Considering status: Mice - genotype confirmed'); # Status is 'Mice - genotype confirmed' if there are any EMI attempts # with status 'Genotype confirmed' - if ( any { $_->{status_name} eq 'Genotype confirmed' } @emi_attempts ) { + if ( any { $_->{status_name} eq 'Genotype confirmed' } @{ $emi_attempts } ) { return 'Mice - genotype confirmed'; } @@ -168,7 +183,7 @@ sub find_project_status { # Status is 'Mice - Microinjection in progress' if there ane any # EMI attempts - if (@emi_attempts) { + if (@{ $emi_attempts }) { return 'Mice - Microinjection in progress'; } @@ -264,13 +279,9 @@ Retrieve any EMI attempts for the ES cells from Tarmits. =cut sub emi_attempts { - my ( $self, $ws_rows ) = @_; - my $status = 'foo'; - - my @es_cell_names = map { $_->{clone_plate_name} . '_' . $_->{clone_well_name} } - grep { $_->{clone_well_id} } @{$ws_rows}; + my ( $self, $es_cell_names ) = @_; - my @es_cell_name_arrays = $self->chunk_array( \@es_cell_names ); + my @es_cell_name_arrays = $self->chunk_array( $es_cell_names ); my @emi_attempts; for my $name_array ( @es_cell_name_arrays ) { @@ -289,9 +300,66 @@ sub emi_attempts { push @emi_attempts, @{ $emi_attempts }; } + # Ignore attempts where MI has been aborted + @emi_attempts = grep { $_->{status_name} ne 'Micro-injection aborted' } @emi_attempts; + return \@emi_attempts; } +=head2 find_allele_names + +Retrieve allele names for the ES cells from Tarmits. + +=cut +sub find_allele_names { + my ( $self, $es_cell_names, $emi_attempts ) = @_; + + my @es_cell_name_arrays = $self->chunk_array( $es_cell_names ); + + my @es_cells; + for my $name_array ( @es_cell_name_arrays ) { + my @query; + for my $es_cell_name ( @{ $name_array } ) { + push @query, ( 'name_in[]' => $es_cell_name ); + } + + my $es_cells = try{ + $self->tarmits_client->find_es_cell( \@query ); + } + catch { + $self->log->error( "Error querying tarmits: $_" ); + return []; + }; + push @es_cells, @{ $es_cells }; + } + # Grab allele information from Tarmits + my @allele_names = uniq grep{ $_ } map { $_->{mgi_allele_symbol_superscript} } @es_cells; + my $allele_products = $self->allele_products( $emi_attempts ); + + return { map { $_ => exists $allele_products->{$_} ? 'Mice' : 'ES Cells' } @allele_names }; +} + +=head2 allele_products + +Work out if allele has Mice or ES Cell products. +Mice will be available if a MI attempt status is set to Genotyping Confirmed + +=cut +sub allele_products { + my ( $self, $emi_attempts ) = @_; + + my @mice = grep{ $_->{status_name} eq 'Genotype confirmed' } @{ $emi_attempts }; + + my %allele_mice; + for my $e ( @mice ) { + if ( $e->{allele_symbol} =~ /(.*)<\/sup>/ ) { + $allele_mice{$1} = $e->{status_name}; + }; + } + + return \%allele_mice; +} + =head2 chunk_array Split up a array into seperate arrays of specific max size @@ -353,7 +421,6 @@ my $sql_query = <<"SQL"; WITH project_requests AS ( SELECT p.id AS project_id, - p.htgt_project_id, p.gene_id, p.targeting_type, pa.mutation_type, @@ -371,13 +438,14 @@ AND p.species_id = 'Mouse' ) SELECT pr.project_id, - pr.htgt_project_id, + s.design_id AS design_id, s.design_gene_id AS gene_id, s.design_gene_symbol AS gene_symbol, s.final_pick_plate_name AS tv_plate_name, s.final_pick_well_name AS tv_well_name, s.final_pick_well_id AS tv_well_id, s.final_pick_well_accepted AS tv_accepted, + s.final_pick_cassette_name AS cassette, s.ep_plate_name AS ep_plate_name, s.ep_well_name AS ep_well_name, s.ep_well_id AS ep_well_id, @@ -426,13 +494,14 @@ AND ( AND s.final_pick_well_id > 0 GROUP BY pr.project_id, - pr.htgt_project_id, + s.design_id, s.design_gene_id, s.design_gene_symbol, s.final_pick_plate_name, s.final_pick_well_name, s.final_pick_well_id, s.final_pick_well_accepted, + s.final_pick_cassette_name, s.ep_plate_name, s.ep_well_name, s.ep_well_id, @@ -471,17 +540,20 @@ sub parse_project_well_summary_results { my %project_well_data; for my $datum ( @{ $query_results } ) { - my $project_id = $datum->{project_id }; + my $project_id = $datum->{project_id}; push @{ $project_well_data{$project_id}{ws} }, $datum; - if ( $datum->{htgt_project_id} && !exists $project_well_data{$project_id}{htgt_project_id} ) { - $project_well_data{$project_id}{htgt_project_id} = $datum->{htgt_project_id}; - } - if ( $datum->{gene_symbol} && !exists $project_well_data{$project_id}{gene} ) { $project_well_data{$project_id}{gene} = $datum->{gene_symbol}; } + if ( $datum->{cassette} && !exists $project_well_data{$project_id}{cassette} ) { + $project_well_data{$project_id}{cassette} = $datum->{cassette}; + } + + if ( $datum->{design_id} && !exists $project_well_data{$project_id}{design_id} ) { + $project_well_data{$project_id}{design_id} = $datum->{design_id}; + } } return \%project_well_data; diff --git a/lib/LIMS2/Model/Util/QCResults.pm b/lib/LIMS2/Model/Util/QCResults.pm index 8ec0ed33a4..c524e371f5 100644 --- a/lib/LIMS2/Model/Util/QCResults.pm +++ b/lib/LIMS2/Model/Util/QCResults.pm @@ -233,79 +233,42 @@ sub retrieve_qc_run_summary_results { my @summary; + my $run_type = 'design_id'; if ($crispr_run) { + $run_type = 'crispr_id'; + } - my %seen_gene; - - foreach my $row ( @{$results} ) { - - my $gene_symbol = $row->{gene_symbol}; - - next unless (!$seen_gene{$gene_symbol} && $gene_symbol); - - $seen_gene{$gene_symbol} = 1; - - my %s = ( - crispr_id => $row->{crispr_id}, - ); - - my @results = reverse sort { - ( $a->{pass} || 0 ) <=> ( $b->{pass} || 0 ) - || ( $a->{num_valid_primers} || 0 ) <=> ( $b->{num_valid_primers} || 0 ) - || ( $a->{valid_primers_score} || 0 ) <=> ( $b->{valid_primers_score} || 0 ) - || ( $a->{score} || 0 ) <=> ( $b->{score} || 0 ) - || ( $a->{num_reads} || 0 ) <=> ( $b->{num_reads} || 0 ) - } - grep { $_->{gene_symbol} and $_->{gene_symbol} eq $gene_symbol } @{$results}; - - if ( my $best = shift @results ) { - $s{plate_name} = $best->{plate_name}; - $s{well_name} = uc $best->{well_name}; - $s{well_name_384} = uc $best->{well_name_384}; - $s{valid_primers} = join( q{,}, @{ $best->{valid_primers} } ); - $s{pass} = $best->{pass}; - $s{gene_symbol} = $best->{gene_symbol}; - } - push @summary, \%s; - } - - } else { - - my $template_well_rs = $qc_run->qc_template->qc_template_wells; - my %seen_design; - - while ( my $template_well = $template_well_rs->next ) { - next - unless $template_well->design_id - and not $seen_design{ $template_well->design_id }++; - + my $template_well_rs = $qc_run->qc_template->qc_template_wells; + my %seen; - my $design_id = $template_well->design_id; + while ( my $template_well = $template_well_rs->next ) { + next + unless $template_well->$run_type + and not $seen{ $template_well->$run_type }++; - my %s = ( - design_id => $template_well->design_id, - ); + my %s = ( + $run_type => $template_well->$run_type, + ); - my @results = reverse sort { - ( $a->{pass} || 0 ) <=> ( $b->{pass} || 0 ) - || ( $a->{num_valid_primers} || 0 ) <=> ( $b->{num_valid_primers} || 0 ) - || ( $a->{valid_primers_score} || 0 ) <=> ( $b->{valid_primers_score} || 0 ) - || ( $a->{score} || 0 ) <=> ( $b->{score} || 0 ) - || ( $a->{num_reads} || 0 ) <=> ( $b->{num_reads} || 0 ) - } - grep { $_->{design_id} and ($_->{design_id} == $template_well->design_id) } @{$results}; - - if ( my $best = shift @results ) { - $s{plate_name} = $best->{plate_name}; - $s{well_name} = uc $best->{well_name}; - $s{well_name_384} = uc $best->{well_name_384}; - $s{valid_primers} = join( q{,}, @{ $best->{valid_primers} } ); - $s{pass} = $best->{pass}; - $s{gene_symbol} = $best->{gene_symbol}; + my @results = reverse sort { + ( $a->{pass} || 0 ) <=> ( $b->{pass} || 0 ) + || ( $a->{num_valid_primers} || 0 ) <=> ( $b->{num_valid_primers} || 0 ) + || ( $a->{valid_primers_score} || 0 ) <=> ( $b->{valid_primers_score} || 0 ) + || ( $a->{score} || 0 ) <=> ( $b->{score} || 0 ) + || ( $a->{num_reads} || 0 ) <=> ( $b->{num_reads} || 0 ) } - push @summary, \%s; + grep { $_->{$run_type} and ($_->{$run_type} == $template_well->$run_type) } @{$results}; + + if ( my $best = shift @results ) { + $s{plate_name} = $best->{plate_name}; + $s{well_name} = uc $best->{well_name}; + $s{well_name_384} = uc $best->{well_name_384}; + $s{valid_primers} = join( q{,}, @{ $best->{valid_primers} } ); + $s{pass} = $best->{pass}; + $s{gene_symbol} = $best->{gene_symbol}; } + push @summary, \%s; } return \@summary; diff --git a/lib/LIMS2/Report/LegacyCreKnockInProjects.pm b/lib/LIMS2/Report/LegacyCreKnockInProjects.pm index 37efcf6753..78a34f5178 100644 --- a/lib/LIMS2/Report/LegacyCreKnockInProjects.pm +++ b/lib/LIMS2/Report/LegacyCreKnockInProjects.pm @@ -16,11 +16,14 @@ override _build_name => sub { override _build_columns => sub { return [ qw( - htgt_project_id - lims2_project_id marker_symbol mgi_gene_id status + allele_names + allele_products + num_alleles + cassette + design_id ) ]; }; diff --git a/lib/LIMS2/WebApp/Controller/API/CrisprQc.pm b/lib/LIMS2/WebApp/Controller/API/CrisprQc.pm index b64ce2a891..b765083fbb 100644 --- a/lib/LIMS2/WebApp/Controller/API/CrisprQc.pm +++ b/lib/LIMS2/WebApp/Controller/API/CrisprQc.pm @@ -15,57 +15,22 @@ API methods dealing with es cell crispr qc =cut -sub update_well_accepted :Path('/api/update_well_accepted') :Args(0) :ActionClass('REST') { +sub update_crispr_es_qc_well :Path('/api/update_crispr_es_qc_well') :Args(0) :ActionClass('REST') { } -sub update_well_accepted_POST { - my ( $self, $c ) = @_; - - my $params = $c->request->params; - - my $qc_well = $c->model('Golgi')->schema->resultset('CrisprEsQcWell')->find( - { - id => $params->{id}, - }, - { prefetch => 'well' } - ); - - #TODO: validate params - - #set both the qc well and the actual well to accepted - try { - $c->model('Golgi')->txn_do( - sub { - $qc_well->update( { accepted => $params->{accepted} } ); - $qc_well->well->update( { accepted => $params->{accepted} } ); - } - ); - $self->status_ok( $c, entity => { success => 1 } ); - } - catch { - $self->status_bad_request( $c, message => "Error: $_" ); - $c->log->error( "Error updating crispr es qc well accepted flag: $_" ); - }; - - return; -} - -sub update_well_crispr_damage :Path('/api/update_well_crispr_damage') :Args(0) :ActionClass('REST') { -} - -sub update_well_crispr_damage_POST { +sub update_crispr_es_qc_well_POST { my ( $self, $c ) = @_; try{ my $qc_well = $c->model('Golgi')->txn_do( sub { - shift->update_crispr_well_damage( $c->request->params ); + shift->update_crispr_es_qc_well( $c->request->params ); } ); $self->status_ok( $c, entity => { success => 1 } ); } catch { - $c->log->error( "Error updating crispr well damage value: $_" ); + $c->log->error( "Error updating crispr es qc well value: $_" ); $self->status_bad_request( $c, message => "Error: $_" ); }; @@ -166,6 +131,36 @@ sub crispr_es_qc_well_POST { ); } +sub validate_crispr_es_qc_run : Path( '/api/validate_crispr_es_qc_run' ) : Args(0) :ActionClass( 'REST' ) { +} + +=head2 POST + +Validate a crispr es qc run + +=cut + +sub validate_crispr_es_qc_run_POST { + my ( $self, $c ) = @_; + + $c->assert_user_roles('edit'); + + try{ + my $crispr_es_qc_run = $c->model('Golgi')->txn_do( + sub { + shift->validate_crispr_es_qc_run( $c->request->params ); + } + ); + $self->status_ok( $c, entity => { success => 1 } ); + } + catch { + $c->log->error( "Error validating crispr es qc run : $_" ); + $self->status_bad_request( $c, message => "Error: $_" ); + }; + + return +} + =head1 LICENSE This library is free software. You can redistribute it and/or modify diff --git a/lib/LIMS2/WebApp/Controller/User/CreateDesign.pm b/lib/LIMS2/WebApp/Controller/User/CreateDesign.pm index 7c1a7a66be..5672c39739 100644 --- a/lib/LIMS2/WebApp/Controller/User/CreateDesign.pm +++ b/lib/LIMS2/WebApp/Controller/User/CreateDesign.pm @@ -471,7 +471,7 @@ sub wge_design_importer :Path( '/user/wge_design_importer' ) : Args(0) { ); my $design_id = $c->request->param('design_id'); - $c->log->info('Importing WGE design: $design_id'); + $c->log->info("wge_design_importer: Importing WGE design: $design_id"); my $design_data = $client->GET( 'design', { id => $design_id, supress_relations => 0 } ); @@ -533,11 +533,11 @@ sub wge_design_importer :Path( '/user/wge_design_importer' ) : Args(0) { assembly_id => $species_default_assembly_id, } ); } - $c->log->debug( "Successfull design creation with id $design_id" ); + $c->log->info( "wge_design_importer: Successfull design creation with id $design_id" ); $c->stash( success_msg => "Successfully imported from WGE design with id $design_id" ); } catch ($err) { - $c->log->warn("Unable to create design: $err"); + $c->log->info("wge_design_importer: Unable to create design: $err"); $c->stash( error_msg => "Error importing WGE design: $err" ); $c->model('Golgi')->txn_rollback; return; diff --git a/lib/LIMS2/WebApp/Controller/User/CrisprQC.pm b/lib/LIMS2/WebApp/Controller/User/CrisprQC.pm index 8823d3f6fe..8ada214c62 100644 --- a/lib/LIMS2/WebApp/Controller/User/CrisprQC.pm +++ b/lib/LIMS2/WebApp/Controller/User/CrisprQC.pm @@ -52,12 +52,13 @@ sub crispr_es_qc_run :Path( '/user/crisprqc/es_qc_run' ) :Args(1) { my @crispr_damage_types = $c->model('Golgi')->schema->resultset( 'CrisprDamageType' )->all; $c->stash( - qc_run_id => $run->id, - seq_project => $run->sequencing_project, - sub_project => $run->sub_project, - species => $run->species_id, - wells => [ sort { $a->{well_name} cmp $b->{well_name} } @qc_wells ], - damage_types => [ map{ $_->id } @crispr_damage_types ], + qc_run_id => $run->id, + seq_project => $run->sequencing_project, + sub_project => $run->sub_project, + species => $run->species_id, + wells => [ sort { $a->{well_name} cmp $b->{well_name} } @qc_wells ], + damage_types => [ map{ $_->id } @crispr_damage_types ], + run_validated => $run->validated, ); return; diff --git a/root/lib/crispr_qc_view.tt b/root/lib/crispr_qc_view.tt index d25f384238..5f8fa37548 100644 --- a/root/lib/crispr_qc_view.tt +++ b/root/lib/crispr_qc_view.tt @@ -34,19 +34,23 @@ [% IF accept %] - + +
+
[% IF row.show_checkbox %] - + [% ELSE %] Accepted in another run [% END %] - +
+
+ [% END %]
- [%- FOR type IN damage_types %] @@ -56,6 +60,14 @@
+ +
+
+ +
+
+ + [% IF row.has_vcf_file %] vcf_file diff --git a/root/lib/navigation.tt b/root/lib/navigation.tt index 57b6944390..9d8dd5807a 100644 --- a/root/lib/navigation.tt +++ b/root/lib/navigation.tt @@ -169,24 +169,9 @@
  • Copy DNA Plate
  • +
  • - +
  • Electroporation Summary @@ -203,14 +188,6 @@ Crispr Electroporation Summary
  • - [% END %] -
  • Cre Knockin ES Distribution Summary @@ -221,6 +198,7 @@ Cre Knockin ES Distribution Genes
  • + [% END %]
  • diff --git a/root/site/user/crisprqc/crispr_es_qc_run.tt b/root/site/user/crisprqc/crispr_es_qc_run.tt index 3daa2ef6c7..250330e897 100644 --- a/root/site/user/crisprqc/crispr_es_qc_run.tt +++ b/root/site/user/crisprqc/crispr_es_qc_run.tt @@ -7,6 +7,10 @@ + + + + @@ -74,6 +45,7 @@ $(document).ready(function() { Crispr ID Alignment Damage Type + Variant Size Variant Files Protein Sequences Reads diff --git a/root/static/js/crispr-es-qc-well-update.js b/root/static/js/crispr-es-qc-well-update.js new file mode 100644 index 0000000000..43b3c58c3e --- /dev/null +++ b/root/static/js/crispr-es-qc-well-update.js @@ -0,0 +1,47 @@ +// update crispr_es_qc_well values, one as a time +// the form textbox or checkbox object that triggers this must have following attributes +// data-crispr_data_type +// data-crispr_well_id +$(document).ready(function() { + $(".update_crispr_es_qc_well").change(function() { + + // give warning class to element + var element = $(this).parent().parent(); + element.removeClass('error').removeClass('success').addClass('warning'); + // grab data type and well_id + var data_type = $(this).attr('data-crispr_data_type'); + var crispr_well_id = $(this).attr('data-crispr_well_id'); + var data = {}; + data['id'] = crispr_well_id; + // if checkbox use checked, otherwise its value + if ( $(this).attr('type') == 'checkbox' ) { + data[data_type] = this.checked; + } + else { + data[data_type] = this.value; + } + + // ajax request to update the crispr es qc well value + $.ajax({ + type: "POST", + url: api_url, + data: data, + success: function(data) { + console.log(data); + element.delay(500).queue(function(){ + $(this).removeClass('warning').removeClass('error').addClass('success').dequeue(); + }); + }, + error: function(data) { + console.log(data); + element.addClass('error'); + element.delay(500).queue(function(){ + $(this).removeClass('warning').removeClass('success').addClass('error').dequeue(); + }); + alert( 'Error updating crispr ' + data_type + ', change not saved' ); + }, + dataType: 'json' + }); + + }); +}); diff --git a/root/static/test/fixtures/reference_data/CrisprDamageType.csv b/root/static/test/fixtures/reference_data/CrisprDamageType.csv index c8d432ef0c..7bd2c205f7 100644 --- a/root/static/test/fixtures/reference_data/CrisprDamageType.csv +++ b/root/static/test/fixtures/reference_data/CrisprDamageType.csv @@ -3,3 +3,4 @@ "frameshift","Damage that causes a frameshift mutation" "in-frame","Damage that does not cause a frameshift mutation" "mosaic","Multiple types of damage" +"no-call","No automatic damage type call" diff --git a/t/lib/LIMS2/t/Model/Plugin/CrisprEsQc.pm b/t/lib/LIMS2/t/Model/Plugin/CrisprEsQc.pm index 74d9feaa97..67fd7ca1c2 100644 --- a/t/lib/LIMS2/t/Model/Plugin/CrisprEsQc.pm +++ b/t/lib/LIMS2/t/Model/Plugin/CrisprEsQc.pm @@ -12,15 +12,15 @@ LIMS2/t/Model/Plugin/CrisprEsQc.pm - test class for LIMS2::Model::Plugin::Crispr =cut -sub update_crispr_well_damage : Tests(9) { - note("Testing update_crispr_well_damage method"); +sub update_crispr_es_qc_well : Tests(16) { + note("Testing update_crispr_es_qc_well method"); ok my $crispr_es_qc_well = model->schema->resultset('CrisprEsQcWell')->find( { id => 1 } ), 'can get crispr es qc well'; throws_ok{ - model->update_crispr_well_damage( - { well_id => $crispr_es_qc_well->id, + model->update_crispr_es_qc_well( + { id => $crispr_es_qc_well->id, damage_type => 'foo' } ) @@ -28,7 +28,7 @@ sub update_crispr_well_damage : Tests(9) { is $crispr_es_qc_well->crispr_damage_type_id, 'mosaic', 'current crispr damage type is mosaic'; - ok model->update_crispr_well_damage( + ok model->update_crispr_es_qc_well( { id => $crispr_es_qc_well->id, damage_type => 'frameshift' } @@ -38,14 +38,59 @@ sub update_crispr_well_damage : Tests(9) { is $crispr_es_qc_well->crispr_damage_type_id, 'frameshift', 'current crispr damage type has changed to frameshift'; - ok model->update_crispr_well_damage( + ok model->update_crispr_es_qc_well( { id => $crispr_es_qc_well->id, + damage_type => '', } ), 'can update crispr damage type to undef'; ok $crispr_es_qc_well->discard_changes, 'refresh row object from db'; is $crispr_es_qc_well->crispr_damage_type_id, undef, 'current crispr damage type has changed to null'; + + ok model->update_crispr_es_qc_well( + { id => $crispr_es_qc_well->id, + variant_size => -20, + } + ), 'can update variant size to -20'; + + ok $crispr_es_qc_well->discard_changes, 'refresh row object from db'; + is $crispr_es_qc_well->variant_size, -20, + 'current variant_size has been updated to -20'; + + ok model->update_crispr_es_qc_well( + { id => $crispr_es_qc_well->id, + variant_size => '', + accepted => 'true', + } + ), 'can update accepted to true and variant_size to null'; + + ok $crispr_es_qc_well->discard_changes, 'refresh row object from db'; + is $crispr_es_qc_well->variant_size, undef, + 'current variant_size has been updated to null'; + is $crispr_es_qc_well->accepted, 1, + 'current accepted has been updated to true'; +} + +sub validated_crispr_es_qc_run : Tests() { + note("Testing validate_crispr_es_qc_run method"); + + ok my $crispr_es_qc_run = model->schema->resultset('CrisprEsQcRuns')->find( { id => 1 } ), + 'can get crispr es qc run'; + is $crispr_es_qc_run->validated, 0, + 'validated set to false for crispr es qc run'; + + throws_ok{ + model->validate_crispr_es_qc_run( { id => 2 } ) + } 'LIMS2::Exception::NotFound', 'throws error when invalid crispr es qc run id used'; + + lives_ok{ + model->validate_crispr_es_qc_run( { id => 1 } ) + } 'can validate crispr_es_qc_run'; + + ok $crispr_es_qc_run->discard_changes, 'refresh row object from db'; + is $crispr_es_qc_run->validated, 1, + 'validated set to true for crispr es qc run'; } 1;