diff --git a/db/00188/AddMemberTypeCvterm.pm b/db/00188/AddMemberTypeCvterm.pm new file mode 100644 index 0000000000..1739fc78bf --- /dev/null +++ b/db/00188/AddMemberTypeCvterm.pm @@ -0,0 +1,73 @@ +#!/usr/bin/env perl + +=head1 NAME + +AddMemberTypeCvterm + +=head1 SYNOPSIS + +mx-run AddMemberTypeCvterm [options] -H hostname -D dbname -u username [-F] + +this is a subclass of L +see the perldoc of parent class for more details. + +=head1 DESCRIPTION +This patch adds member_type stock_property cvterm +This subclass uses L. The parent class uses L + +=head1 AUTHOR + +Titima Tantikanjana + +=head1 COPYRIGHT & LICENSE + +Copyright 2010 Boyce Thompson Institute for Plant Research + +This program is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + + +package AddMemberTypeCvterm; + +use Moose; +use Bio::Chado::Schema; +use Try::Tiny; +extends 'CXGN::Metadata::Dbpatch'; + + +has '+description' => ( default => <<'' ); +This patch adds the 'member_type' stock_property cvterm + +has '+prereq' => ( + default => sub { + [], + }, + +); + +sub patch { + my $self=shift; + + print STDOUT "Executing the patch:\n " . $self->name . ".\n\nDescription:\n ". $self->description . ".\n\nExecuted by:\n " . $self->username . " ."; + + print STDOUT "\nChecking if this db_patch was executed before or if previous db_patches have been executed.\n"; + + print STDOUT "\nExecuting the SQL commands.\n"; + my $schema = Bio::Chado::Schema->connect( sub { $self->dbh->clone } ); + + print STDERR "INSERTING CV TERMS...\n"; + + $schema->resultset("Cv::Cvterm")->create_with({ + name => 'member_type', + cv => 'stock_property' + }); + + print "You're done!\n"; +} + + +#### +1; # +#### diff --git a/js/source/legacy/CXGN/BreedersToolbox/Accessions.js b/js/source/legacy/CXGN/BreedersToolbox/Accessions.js index 26e48cfef0..ac73627308 100644 --- a/js/source/legacy/CXGN/BreedersToolbox/Accessions.js +++ b/js/source/legacy/CXGN/BreedersToolbox/Accessions.js @@ -37,18 +37,73 @@ function enable_ui() { jQuery(document).ready(function ($) { jQuery('#manage_accessions_populations_new').click(function(){ - jQuery("#create_population_list_div").html(list.listSelect("create_population_list_div", ["accessions"], undefined, undefined, undefined )); jQuery('#manage_populations_add_population_dialog').modal('show'); }); jQuery("#create_population_submit").click(function(){ + const lo = new CXGN.List(); + + const population_name = jQuery('#create_population_name').val(); + if (!population_name) { + alert ("Population name is required"); + return; + } + + const member_type = jQuery('#member_type').val(); + if (!member_type) { + alert ("Member type is required"); + return; + } + + let list_id; + let list_validation = 1; + + if (member_type == 'accessions') { + list_id = jQuery('#create_population_accession_list_div_list_select').val(); + if (!list_id) { + alert ("A list of accessions is required"); + return; + } else { + list_validation = lo.legacy_validate(list_id, 'accessions', true); + if (list_validation != 1) { + alert("The accession list did not pass validation. Names in the list must be uniquenames with accession stock type in the database"); + return; + } + } + } else if (member_type == 'plots') { + list_id = jQuery('#create_population_plot_list_div_list_select').val(); + if (!list_id) { + alert ("A list of plots is required"); + return; + } else { + list_validation = lo.legacy_validate(list_id, 'plots', true); + if (list_validation != 1) { + alert("The plot list did not pass validation. Names in the list must be uniquenames with plot stock type in the database"); + return; + } + } + } else if (member_type == 'plants') { + list_id = jQuery('#create_population_plant_list_div_list_select').val(); + if (!list_id) { + alert ("A list of plants is required"); + return; + } else { + list_validation = lo.legacy_validate(list_id, 'plants', true); + if (list_validation != 1) { + alert("The plant list did not pass validation. Names in the list must be uniquenames with plant stock type in the database"); + return; + } + } + } + jQuery.ajax({ type: 'POST', url: '/ajax/population/new', dataType: "json", data: { - 'population_name': jQuery('#create_population_name').val(), - 'accession_list_id': jQuery('#create_population_list_div_list_select').val(), + 'population_name': population_name, + 'member_type': member_type, + 'list_id': list_id, }, beforeSend: function(){ disable_ui(); @@ -91,10 +146,11 @@ jQuery(document).ready(function ($) { var name = populations[i].name; var population_id = populations[i].stock_id; var accessions = populations[i].members; - var table_id = name+i+"_pop_table"; + var member_type = populations[i].member_type + var table_id = population_id+i+"_pop_table"; var section_html = '
'; @@ -119,7 +175,8 @@ jQuery(document).ready(function ($) { ajax: '/ajax/manage_accessions/population_members/'+population_id, destroy: true, columns: [ - { title: "Accession Name", "data": null, "render": function ( data, type, row ) { return ""+row.name+""; } }, + { title: "Member Name", "data": null, "render": function ( data, type, row ) { return ""+row.name+""; } }, + { title: "Member Type", "data": "stock_type" }, { title: "Description", "data": "description" }, { title: "Synonyms", "data": "synonyms[, ]" }, { title: "Remove From Population", "data": null, "render": function ( data, type, row ) { return "X"; } }, @@ -143,13 +200,22 @@ jQuery(document).ready(function ($) { var population_id; var population_name; + var member_type; + + jQuery(document).on("click", "a[name='manage_populations_add_members']", function(){ - jQuery(document).on("click", "a[name='manage_populations_add_accessions']", function(){ population_id = jQuery(this).data('population_id'); population_name = jQuery(this).data('population_name'); - jQuery("#add_accession_to_population_list_div").html(list.listSelect("add_accession_to_population_list_div", ["accessions"], undefined, undefined, undefined)); - jQuery('#add_accession_population_name').html(population_name); - jQuery('#manage_populations_add_accessions_dialog').modal('show'); + member_type = jQuery(this).data('member_type'); + if (member_type == 'plots') { + jQuery("#add_member_to_population_list_div").html(list.listSelect("add_member_to_population_list_div", ['plots'], 'select a list of plots', undefined, undefined)); + } else if (member_type == 'plants') { + jQuery("#add_member_to_population_list_div").html(list.listSelect("add_member_to_population_list_div", ['plants'], 'select a list of plants', undefined, undefined)); + } else { + jQuery("#add_member_to_population_list_div").html(list.listSelect("add_member_to_population_list_div", ['accessions'], 'select a list of accessions', undefined, undefined)); + } + jQuery('#add_member_population_name').html(population_name); + jQuery('#manage_populations_add_members_dialog').modal('show'); }); jQuery(document).on("click", "a[name='manage_populations_delete_population']", function(){ @@ -167,14 +233,64 @@ jQuery(document).ready(function ($) { source: '/ajax/stock/population_autocomplete', }); - jQuery("#add_accessions_to_population_submit").click(function(){ + jQuery("#add_members_to_population_submit").click(function(){ + const lo = new CXGN.List(); + let list_validation = 1; + const member_list_id = jQuery('#add_member_to_population_list_div_list_select').val(); + + if (member_type == 'accessions') { + if (!member_list_id) { + alert ("A list of accessions is required"); + return; + } else { + list_validation = lo.legacy_validate(member_list_id, 'accessions', true); + if (list_validation != 1) { + alert("The accession list did not pass validation. Names in the list must be uniquenames with accession stock type in the database"); + return; + } + } + } else if (member_type == 'plots') { + if (!member_list_id) { + alert ("A list of plots is required"); + return; + } else { + list_validation = lo.legacy_validate(member_list_id, 'plots', true); + if (list_validation != 1) { + alert("The plot list did not pass validation. Names in the list must be uniquenames with plot stock type in the database"); + return; + } + } + } else if (member_type == 'plants') { + if (!member_list_id) { + alert ("A list of plants is required"); + return; + } else { + list_validation = lo.legacy_validate(member_list_id, 'plants', true); + if (list_validation != 1) { + alert("The plant list did not pass validation. Names in the list must be uniquenames with plant stock type in the database"); + return; + } + } + } else { + if (!member_list_id) { + alert ("A list of accessions is required"); + return; + } else { + list_validation = lo.legacy_validate(member_list_id, 'accessions', true); + if (list_validation != 1) { + alert("The accession list did not pass validation. Names in the list must be in the database"); + return; + } + } + } + jQuery.ajax({ type: 'POST', - url: '/ajax/population/add_accessions', + url: '/ajax/population/add_members', dataType: "json", data: { 'population_name': population_name, - 'accession_list_id': jQuery('#add_accession_to_population_list_div_list_select').val(), + 'list_id': jQuery('#add_member_to_population_list_div_list_select').val(), }, beforeSend: function(){ disable_ui(); @@ -189,7 +305,7 @@ jQuery(document).ready(function ($) { } }, error: function () { - alert('An error occurred in adding accessions to population. sorry'); + alert('An error occurred in adding members to population. sorry'); } }); }); diff --git a/js/source/legacy/CXGN/BreedersToolbox/Crosses.js b/js/source/legacy/CXGN/BreedersToolbox/Crosses.js index c760b9b85a..a5a50499d5 100644 --- a/js/source/legacy/CXGN/BreedersToolbox/Crosses.js +++ b/js/source/legacy/CXGN/BreedersToolbox/Crosses.js @@ -83,6 +83,7 @@ jQuery(document).ready(function($) { $("#get_maternal_parent").toggle(($("#cross_type").val() == "biparental") || ($("#cross_type").val() == "backcross") || ($("#cross_type").val() == "sib")); $("#get_paternal_parent").toggle(($("#cross_type").val() == "biparental") || ($("#cross_type").val() == "backcross") || ($("#cross_type").val() == "sib")); $("#exact_parents").toggle(($("#cross_type").val() == "biparental") || ($("#cross_type").val() == "backcross") || ($("#cross_type").val() == "sib")); + $("#exact_female").toggle($("#cross_type").val() == "open"); $("#get_selfed_parent").toggle($("#cross_type").val() == "self"); $("#get_open_maternal_parent").toggle($("#cross_type").val() == "open"); $("#get_open_paternal_population").toggle($("#cross_type").val() == "open"); @@ -92,7 +93,7 @@ jQuery(document).ready(function($) { $("#get_bulk_open_maternal_population").toggle($("#cross_type").val() == "bulk_open"); $("#get_bulk_open_paternal_population").toggle($("#cross_type").val() == "bulk_open"); $("#get_doubled_haploid_parent").toggle($("#cross_type").val() == "doubled_haploid"); - $("#get_dihaploid_induction_parent").toggle($("#cross_type").val() == "dihaploid_induction"); + $("#get_dihaploid_induction_parent").toggle($("#cross_type").val() == "dihaploid_induction"); $("#polycross_accessions").toggle($("#cross_type").val() == "polycross"); $("#reciprocal_accessions").toggle($("#cross_type").val() == "reciprocal"); $("#maternal_accessions").toggle($("#cross_type").val() == "multicross"); @@ -165,11 +166,16 @@ jQuery(document).ready(function($) { } var visibleToRole = $("#visible_to_role").val(); - var female_plot = $("#female_plot").val(); - var male_plot = $("#male_plot").val(); - var cross_combination = $("#dialog_cross_combination").val(); + let female_plot_plant; + if (crossType == 'open') { + female_plot_plant = jQuery("#open_female_plot_plant").val(); + } else { + female_plot_plant = jQuery("#female_plot_plant").val(); + } + const male_plot_plant = $("#male_plot_plant").val(); + const cross_combination = $("#dialog_cross_combination").val(); - add_cross(crossType, crossName, crossing_trial_id, visibleToRole, female_plot, male_plot, cross_combination); + add_cross(crossType, crossName, crossing_trial_id, visibleToRole, female_plot_plant, male_plot_plant, cross_combination); }); @@ -193,89 +199,10 @@ jQuery(document).ready(function($) { get_select_box('projects', 'upload_crosses_crossing_experiment_select_div', { 'name' : 'upload_crosses_crossing_experiment_id', 'id' : 'upload_crosses_crossing_experiment_id', 'breeding_program_id' : breeding_program_id, 'get_crossing_trials': '1', 'empty':1}); }); - jQuery("#cross_accession_info_format").click(function() { + jQuery("#upload_crosses_spreadsheet_format_info").click(function() { jQuery("#cross_spreadsheet_info_dialog").modal("show"); - jQuery("#cross_parents_header").show(); - jQuery("#cross_plot_parents_header").hide(); - jQuery("#cross_plant_parents_header").hide(); - jQuery("#accession_parent_info").show(); - jQuery("#plot_parent_info").hide(); - jQuery("#plant_parent_info").hide(); - jQuery("#any_parent_info").hide(); }); - jQuery("#cross_plot_info_format").click(function() { - jQuery("#cross_spreadsheet_info_dialog").modal("show"); - jQuery("#cross_parents_header").hide(); - jQuery("#cross_plot_parents_header").show(); - jQuery("#cross_plant_parents_header").hide(); - jQuery("#accession_parent_info").hide(); - jQuery("#plot_parent_info").show(); - jQuery("#plant_parent_info").hide(); - jQuery("#any_parent_info").hide(); - }); - - jQuery("#cross_plant_info_format").click(function() { - jQuery("#cross_spreadsheet_info_dialog").modal("show"); - jQuery("#cross_parents_header").hide(); - jQuery("#cross_plot_parents_header").hide(); - jQuery("#cross_plant_parents_header").show(); - jQuery("#accession_parent_info").hide(); - jQuery("#plot_parent_info").hide(); - jQuery("#plant_parent_info").show(); - jQuery("#any_parent_info").hide(); - }); - - jQuery("#cross_simplified_parent_info_format").click(function() { - jQuery("#cross_spreadsheet_info_dialog").modal("show"); - jQuery("#cross_parents_header").show(); - jQuery("#cross_plot_parents_header").hide(); - jQuery("#cross_plant_parents_header").hide(); - jQuery("#accession_parent_info").hide(); - jQuery("#plot_parent_info").hide(); - jQuery("#plant_parent_info").hide(); - jQuery("#any_parent_info").show(); - }); - - jQuery("#cross_file_format_option").change(function(){ - if (jQuery(this).val() == ""){ - jQuery("#xls_cross_accession_section").hide(); - jQuery("#xls_cross_plot_section").hide(); - jQuery("#xls_cross_plant_section").hide(); - jQuery("#xls_cross_simplified_section").hide(); - jQuery("#submit_button_section").hide(); - } - if (jQuery(this).val() == "xls_cross_accession"){ - jQuery("#xls_cross_accession_section").show(); - jQuery("#xls_cross_plot_section").hide(); - jQuery("#xls_cross_plant_section").hide(); - jQuery("#xls_cross_simplified_section").hide(); - jQuery("#submit_button_section").show(); - } - if(jQuery(this).val() == "xls_cross_plot"){ - jQuery("#xls_cross_plot_section").show(); - jQuery("#xls_cross_accession_section").hide(); - jQuery("#xls_cross_plant_section").hide(); - jQuery("#xls_cross_simplified_section").hide(); - jQuery("#submit_button_section").show(); - } - if (jQuery(this).val() == "xls_cross_plant" ){ - jQuery("#xls_cross_plant_section").show(); - jQuery("#xls_cross_plot_section").hide(); - jQuery("#xls_cross_accession_section").hide(); - jQuery("#xls_cross_simplified_section").hide(); - jQuery("#submit_button_section").show(); - } - if (jQuery(this).val() == "xls_cross_simplified" ){ - jQuery("#xls_cross_simplified_section").show(); - jQuery("#xls_cross_plant_section").hide(); - jQuery("#xls_cross_plot_section").hide(); - jQuery("#xls_cross_accession_section").hide(); - jQuery("#submit_button_section").show(); - } - }); - - $("#upload_crosses_submit").click(function() { upload_crosses_file(); }); @@ -320,7 +247,7 @@ jQuery(document).ready(function($) { }); - function add_cross(crossType, crossName, crossing_trial_id, visibleToRole, female_plot, male_plot, cross_combination) { + function add_cross(crossType, crossName, crossing_trial_id, visibleToRole, female_plot_plant, male_plot_plant, cross_combination) { var progenyNumber = $("#progeny_number").val(); var prefix = $("#prefix").val(); @@ -368,7 +295,7 @@ jQuery(document).ready(function($) { maternal = doubledHaploidParent; paternal = doubledHaploidParent; break; - case 'dihaploid_induction': + case 'dihaploid_induction': var dihaploidInductionParent = $("#dihaploid_induction_parent").val(); maternal = dihaploidInductionParent; paternal = dihaploidInductionParent; @@ -419,8 +346,8 @@ jQuery(document).ready(function($) { 'suffix': suffix, 'visible_to_role': visibleToRole, 'crossing_trial_id': crossing_trial_id, - 'female_plot': female_plot, - 'male_plot': male_plot, + 'female_plot_plant': female_plot_plant, + 'male_plot_plant': male_plot_plant, 'cross_combination': cross_combination, }, beforeSend: function() { @@ -446,32 +373,28 @@ jQuery(document).ready(function($) { } function upload_crosses_file() { - var crossing_trial_id = $("#upload_crosses_crossing_experiment_id").val(); - var experiment_id = $("#experiment_id").val(); + const breeding_program = jQuery("#upload_crosses_breeding_program_id").val(); + if (!breeding_program) { + alert("Please select your breeding program"); + return; + } + const crossing_trial_id = jQuery("#upload_crosses_crossing_experiment_id").val(); + const experiment_id = jQuery("#experiment_id").val(); if (!crossing_trial_id && !experiment_id) { alert("A crossing experiment is required"); return; } - var uploadFileXlsSimple = $("#xls_crosses_simple_file").val(); - if (uploadFileXlsSimple === ''){ - var uploadFileXlsPlots = $("#xls_crosses_plots_file").val(); - if (uploadFileXlsPlots === ''){ - var uploadFileXlsPlants = $("#xls_crosses_plants_file").val(); - if (uploadFileXlsPlants === '') { - var uploadFileXlsSimplifiedParents = $("#xls_crosses_simplified_parents_file").val(); - if (uploadFileXlsSimplifiedParents === ''){ - alert("Please select a file"); - return; - } - } - } + const uploadFile = jQuery("#upload_crosses_file").val(); + if (!uploadFile) { + alert("Please select a file"); + return; } - $('#upload_crosses_form').attr("action", "/ajax/cross/upload_crosses_file"); + jQuery('#upload_crosses_form').attr("action", "/ajax/cross/upload_crosses_file"); - $("#upload_crosses_form").submit(); + jQuery("#upload_crosses_form").submit(); } function get_accession_names(accession_select_id) { @@ -533,13 +456,11 @@ jQuery(document).ready(function($) { }, success: function(response) { if (response.error) { + jQuery("#working_modal").modal("hide"); alert(response.error); + } else { refreshCrossJsTree(0); -// get_select_box('crosses', 'upload_crosses_select_crossingtrial_1', {'id':'upload_crosses_select_crossingtrial_1_sel', 'name':'upload_crosses_select_crossingtrial_1_sel', 'multiple':0}); -// get_select_box('crosses', 'upload_crosses_select_crossingtrial_2', {'id':'cross_upload_crossing_trial', 'name':'cross_upload_crossing_trial', 'multiple':0}); -// get_select_box('crosses', 'upload_crosses_select_crossingtrial_3', {'id':'upload_crosses_select_crossingtrial_3_sel', 'name':'upload_crosses_select_crossingtrial_3_sel', 'multiple':0}); -// get_select_box('crosses', 'upload_crosses_select_crossingtrial_4', {'id':'crossing_trial', 'name':'crossing_trial', 'multiple':0}); Workflow.focus("#add_crossing_trial_workflow", -1); //Go to success page Workflow.check_complete("#add_crossing_trial_workflow"); jQuery("#working_modal").modal("hide"); diff --git a/lib/CXGN/BreedersToolbox/Accessions.pm b/lib/CXGN/BreedersToolbox/Accessions.pm index 7e0bafdf9b..05ef1f0950 100644 --- a/lib/CXGN/BreedersToolbox/Accessions.pm +++ b/lib/CXGN/BreedersToolbox/Accessions.pm @@ -67,6 +67,7 @@ sub get_all_populations { my $population_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'population', 'stock_type'); my $population_member_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'member_of', 'stock_relationship'); + my $member_type_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'member_type', 'stock_property')->cvterm_id(); my $populations_rs = $schema->resultset("Stock::Stock")->search({ 'type_id' => $population_cvterm->cvterm_id(), @@ -81,6 +82,15 @@ sub get_all_populations { $population_info{'description'}=$population_row->description(); $population_info{'stock_id'}=$population_row->stock_id(); + my $member_type; + my $member_type_row = $schema->resultset("Stock::Stockprop")->find({ stock_id => $population_row->stock_id(), type_id => $member_type_cvterm_id }); + if($member_type_row) { + $member_type = $member_type_row->value(); + } else { + $member_type = 'accessions'; + } + $population_info{'member_type'} = $member_type; + push @accessions_by_population, \%population_info; } @@ -93,7 +103,7 @@ sub get_population_members { my $schema = $self->schema(); my $population_member_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'member_of', 'stock_relationship'); - my @accessions_in_population; + my @members_in_population; my $population_members = $schema->resultset("Stock::Stock")->search( { 'object.stock_id'=> $population_stock_id, @@ -103,11 +113,14 @@ sub get_population_members { ); while (my $population_member_row = $population_members->next()) { - my %accession_info; - $accession_info{'stock_relationship_id'}=$population_member_row->get_column('stock_relationship_id'); - $accession_info{'name'}=$population_member_row->name(); - $accession_info{'description'}=$population_member_row->description(); - $accession_info{'stock_id'}=$population_member_row->stock_id(); + my %member_info; + $member_info{'stock_relationship_id'}=$population_member_row->get_column('stock_relationship_id'); + $member_info{'name'}=$population_member_row->name(); + $member_info{'description'}=$population_member_row->description(); + $member_info{'stock_id'}=$population_member_row->stock_id(); + + my $stock_type = $schema->resultset('Cv::Cvterm')->find({ cvterm_id => $population_member_row->type_id()})->name(); + $member_info{'stock_type'}=$stock_type; my $synonyms_rs; $synonyms_rs = $population_member_row->search_related('stockprops', {'type.name' => {ilike => '%synonym%' } }, { join => 'type' }); my @synonyms; @@ -116,10 +129,10 @@ sub get_population_members { push @synonyms, $synonym_row->value(); } } - $accession_info{'synonyms'}=\@synonyms; - push @accessions_in_population, \%accession_info; + $member_info{'synonyms'}=\@synonyms; + push @members_in_population, \%member_info; } - return \@accessions_in_population; + return \@members_in_population; } sub get_possible_seedlots { diff --git a/lib/CXGN/List/Validate/Plugin/AccessionsOrPopulationsOrPlotsOrPlants.pm b/lib/CXGN/List/Validate/Plugin/AccessionsOrPopulationsOrCrossesOrPlotsOrPlants.pm similarity index 75% rename from lib/CXGN/List/Validate/Plugin/AccessionsOrPopulationsOrPlotsOrPlants.pm rename to lib/CXGN/List/Validate/Plugin/AccessionsOrPopulationsOrCrossesOrPlotsOrPlants.pm index 7d6320f6de..be541022d3 100644 --- a/lib/CXGN/List/Validate/Plugin/AccessionsOrPopulationsOrPlotsOrPlants.pm +++ b/lib/CXGN/List/Validate/Plugin/AccessionsOrPopulationsOrCrossesOrPlotsOrPlants.pm @@ -1,4 +1,4 @@ -package CXGN::List::Validate::Plugin::AccessionsOrPopulationsOrPlotsOrPlants; +package CXGN::List::Validate::Plugin::AccessionsOrPopulationsOrCrossesOrPlotsOrPlants; use Moose; @@ -6,7 +6,7 @@ use Data::Dumper; use SGN::Model::Cvterm; sub name { - return "accessions_or_populations_or_plots_or_plants"; + return "accessions_or_populations_or_crosses_or_plots_or_plants"; } sub validate { @@ -18,11 +18,12 @@ sub validate { my $population_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'population', 'stock_type')->cvterm_id(); my $plot_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot', 'stock_type')->cvterm_id(); my $plant_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant', 'stock_type')->cvterm_id(); + my $cross_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'cross', 'stock_type')->cvterm_id(); my @missing = (); foreach my $l (@$list) { my $rs = $schema->resultset("Stock::Stock")->search({ - type_id=> [$accession_type_id, $population_type_id, $plot_type_id, $plant_type_id], + type_id=> [$accession_type_id, $population_type_id, $cross_type_id, $plot_type_id, $plant_type_id], uniquename => $l, is_obsolete => {'!=' => 't'}, }); diff --git a/lib/CXGN/Pedigree/AddCrosses.pm b/lib/CXGN/Pedigree/AddCrosses.pm index 544f1ba7ef..643b2a4035 100644 --- a/lib/CXGN/Pedigree/AddCrosses.pm +++ b/lib/CXGN/Pedigree/AddCrosses.pm @@ -460,7 +460,7 @@ sub _validate_cross { $female_parent = $self->_get_accession_or_cross($female_parent_name); $male_parent = $self->_get_accession_or_cross($male_parent_name); - + if (!$female_parent || !$male_parent) { print STDERR "Parent $female_parent_name or $male_parent_name in pedigree is not a stock\n"; return; diff --git a/lib/CXGN/Pedigree/AddPopulations.pm b/lib/CXGN/Pedigree/AddPopulations.pm index 0c7f6a2b62..a8668d76d2 100644 --- a/lib/CXGN/Pedigree/AddPopulations.pm +++ b/lib/CXGN/Pedigree/AddPopulations.pm @@ -59,6 +59,14 @@ has 'members' => ( required => 1, ); +has 'member_type' => ( + isa =>'Str', + is => 'rw', + predicate => 'has_member_type', + default => 'accessions' +); + + sub add_population { my $self = shift; my $schema = $self->get_schema(); @@ -66,12 +74,17 @@ sub add_population { my $phenome_schema = $self->get_phenome_schema(); my $user_id = $self->get_user_id(); my @members = @{$self->get_members()}; + my $member_type = $self->get_member_type(); my $error; my $accession_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'accession', 'stock_type')->cvterm_id(); + my $plot_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot', 'stock_type')->cvterm_id(); + my $plant_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant', 'stock_type')->cvterm_id(); my $population_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'population', 'stock_type')->cvterm_id(); my $synonym_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'stock_synonym', 'stock_property')->cvterm_id(); my $member_of_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'member_of', 'stock_relationship')->cvterm_id(); + my $member_type_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'member_type', 'stock_property'); + my @stock_types = ($accession_cvterm_id, $plot_cvterm_id, $plant_cvterm_id); my $population_id; my $previous_pop_rs = $schema->resultset("Stock::Stock")->search({ @@ -85,7 +98,7 @@ sub add_population { my $acc_synonym_rs = $schema->resultset("Stock::Stock")->search({ 'me.is_obsolete' => { '!=' => 't' }, 'stockprops.value' => { -in => \@members}, - 'me.type_id' => $accession_cvterm_id, + 'me.type_id' => { -in => \@stock_types}, 'stockprops.type_id' => $synonym_cvterm_id },{join => 'stockprops', '+select'=>['stockprops.value'], '+as'=>['synonym']}); my %acc_synonyms_lookup; @@ -102,6 +115,8 @@ sub add_population { }); $population_id = $pop_rs->stock_id(); + $pop_rs->create_stockprops({$member_type_cvterm->name() => $member_type}); + # generate population connections to the members foreach my $m (@members) { if (exists($acc_synonyms_lookup{$m})) { @@ -140,7 +155,7 @@ sub add_population { return { success => "Success! Population $population_name created", population_id=>$population_id }; } -sub add_accessions { +sub add_members { my $self = shift; my $schema = $self->get_schema(); my $population_name = $self->get_name(); @@ -169,11 +184,11 @@ sub add_accessions { $error = $_; }; if ($error) { - print STDERR "Error adding accessions to population $population_name: $error\n"; - return { error => "Error adding accessions to population $population_name: $error" }; + print STDERR "Error adding members to population $population_name: $error\n"; + return { error => "Error adding members to population $population_name: $error" }; } else { - print STDERR "Accession added to population $population_name successfully\n"; - return { success => "Accession added to population $population_name successfully!" }; + print STDERR "Member added to population $population_name successfully\n"; + return { success => "Member added to population $population_name successfully!" }; } } diff --git a/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesExcelFormat.pm b/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesExcelFormat.pm index 0a564bd5f3..22ec6be98e 100644 --- a/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesExcelFormat.pm +++ b/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesExcelFormat.pm @@ -8,6 +8,8 @@ use SGN::Model::Cvterm; use Data::Dumper; use CXGN::List::Validate; +# DEPRECATED: This plugin has been replaced by the CrossesGeneric plugin + sub _validate_with_plugin { my $self = shift; my $filename = $self->get_filename(); diff --git a/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesGeneric.pm b/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesGeneric.pm new file mode 100644 index 0000000000..f42b01c011 --- /dev/null +++ b/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesGeneric.pm @@ -0,0 +1,243 @@ +package CXGN::Pedigree::ParseUpload::Plugin::CrossesGeneric; + +use Moose::Role; +use CXGN::File::Parse; +use CXGN::Stock::StockLookup; +use SGN::Model::Cvterm; +use Data::Dumper; +use CXGN::List::Validate; + +sub _validate_with_plugin { + my $self = shift; + + my $filename = $self->get_filename(); + my $schema = $self->get_chado_schema(); + my $cross_additional_info = $self->get_cross_additional_info(); + + my @error_messages; + my %errors; + my @optional_columns = ('male_parent', 'cross_combination'); + push @optional_columns, @$cross_additional_info; + + my $parser = CXGN::File::Parse->new ( + file => $filename, + required_columns => [ 'cross_unique_id', 'cross_type', 'female_parent'], + optional_columns => \@optional_columns, + column_aliases => { + 'cross_unique_id' => ['cross unique id'], + 'cross_type' => ['cross type', 'type'], + 'female_parent' => ['female', 'female parent'], + 'male_parent' => ['male parent', 'male'], + 'cross_combination' => ['cross combination'] + } + ); + my $parsed = $parser->parse(); + my $parsed_errors = $parsed->{errors}; + my $parsed_columns = $parsed->{columns}; + my $parsed_data = $parsed->{data}; + my $parsed_values = $parsed->{values}; + my $additional_columns = $parsed->{additional_columns}; + + # return if parsing error + if ( $parsed_errors && scalar(@$parsed_errors) > 0 ) { + $errors{'error_messages'} = $parsed_errors; + $self->_set_parse_errors(\%errors); + return; + } + + if ( $additional_columns && scalar(@$additional_columns) > 0 ) { + $errors{'error_messages'} = [ + "The following columns are not recognized: " . join(', ', @$additional_columns) . ". Please check the spreadsheet format for the allowed columns." + ]; + $self->_set_parse_errors(\%errors); + return; + } + + #currently supported cross types + my %supported_cross_types; + $supported_cross_types{'biparental'} = 1; #both parents required + $supported_cross_types{'self'} = 1; #only female parent required + $supported_cross_types{'open'} = 1; #only female parent required + $supported_cross_types{'sib'} = 1; #both parents required but can be the same. + $supported_cross_types{'bulk_self'} = 1; #only female population required + $supported_cross_types{'bulk_open'} = 1; #only female population required + $supported_cross_types{'bulk'} = 1; #both female population and male accession required + $supported_cross_types{'doubled_haploid'} = 1; #only female parent required + $supported_cross_types{'dihaploid_induction'} = 1; # ditto + $supported_cross_types{'polycross'} = 1; #both parents required + $supported_cross_types{'backcross'} = 1; #both parents required, parents can be cross or accession stock type + + my $seen_crosses = $parsed_values->{'cross_unique_id'}; + my $seen_cross_types = $parsed_values->{'cross_type'}; + my $seen_female_parents = $parsed_values->{'female_parent'}; + my $seen_male_parents = $parsed_values->{'male_parent'}; + my @all_parents; + push @all_parents, @$seen_female_parents; + push @all_parents, @$seen_male_parents; + + foreach my $type (@$seen_cross_types) { + if (!exists $supported_cross_types{$type}) { + push @error_messages, "Cross type not supported: $type. Cross type should be biparental, self, open, sib, bulk_self, bulk_open, backcross, polycross, doubled_haploid or dihaploid_induction"; + } + } + + my $parent_validator = CXGN::List::Validate->new(); + my @parents_missing = @{$parent_validator->validate($schema,'accessions_or_populations_or_crosses_or_plots_or_plants',\@all_parents)->{'missing'}}; + + if (scalar(@parents_missing) > 0) { + push @error_messages, "The following parents are not in the database, or are not in the database as accession names, population names, cross unique ids, plot names or plant names: ".join(',',@parents_missing); + } + + my $rs = $schema->resultset("Stock::Stock")->search({ + 'is_obsolete' => { '!=' => 't' }, + 'uniquename' => { -in => $seen_crosses } + }); + + while (my $r=$rs->next){ + push @error_messages, "Cross unique id already exists in database: ".$r->uniquename; + } + + foreach my $row (@$parsed_data) { + my $cross_name = $row->{'cross_unique_id'}; + my $female_parent = $row->{'female_parent'}; + my $male_parent = $row->{'male_parent'}; + my $cross_type = $row->{'cross_type'}; + my $line_number = $row->{'_row'}; + + if ($female_parent eq $male_parent) { + if ($cross_type ne 'self' && $cross_type ne 'sib' && $cross_type ne 'doubled_haploid' && $cross_type ne 'dihaploid_induction'){ + push @error_messages, "Female parent and male parent are the same on line $line_number, but cross type is not self, sib, doubled_haploid or dihaploid_induction."; + } + } + if ($cross_type eq 'biparental') { + if (!$male_parent){ + push @error_messages, "For $cross_name on line number $line_number, Cross Type is biparental, but no male parent given"; + } + } + if($cross_type eq 'backcross') { + if (!$male_parent){ + push @error_messages, "For $cross_name on line number $line_number, Cross Type is backcross, but no male parent given"; + } + } + if($cross_type eq "sib") { + if (!$male_parent){ + push @error_messages, "For $cross_name on line number $line_number, Cross Type is sib, but no male parent given"; + } + } + if($cross_type eq "polycross") { + if (!$male_parent){ + push @error_messages, "For $cross_name on line number $line_number, Cross Type is polycross, but no male parent given"; + } + } + if($cross_type eq "bulk") { + if (!$male_parent){ + push @error_messages, "For $cross_name on line number $line_number, Cross Type is bulk, but no male parent given"; + } + } + } + + if (scalar(@error_messages) >= 1) { + $errors{'error_messages'} = \@error_messages; + $self->_set_parse_errors(\%errors); + return; + } + + $self->_set_parsed_data($parsed); + return 1; + +} + +sub _parse_with_plugin { + my $self = shift; + my $schema = $self->get_chado_schema(); + my $parsed = $self->_parsed_data(); + my $parsed_data = $parsed->{data}; + my $cross_additional_info_headers = $self->get_cross_additional_info(); + my %cross_additional_info_hash; + my @pedigrees; + my %parsed_result; + + my $accession_stock_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'accession', 'stock_type')->cvterm_id(); + my $plot_stock_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot', 'stock_type')->cvterm_id(); + my $plant_stock_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant', 'stock_type')->cvterm_id(); + my $plot_of_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot_of', 'stock_relationship')->cvterm_id(); + my $plant_of_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant_of', 'stock_relationship')->cvterm_id(); + + foreach my $row (@$parsed_data) { + my $cross_name = $row->{'cross_unique_id'}; + my $female_parent = $row->{'female_parent'}; + my $male_parent = $row->{'male_parent'}; + my $cross_type = $row->{'cross_type'}; + my $cross_combination = $row->{'cross_combination'}; + + my $pedigree = Bio::GeneticRelationships::Pedigree->new(name=>$cross_name, cross_type=>$cross_type, cross_combination=>$cross_combination); + + my $female_rs = $schema->resultset("Stock::Stock")->find({uniquename => $female_parent}); + my $female_stock_id = $female_rs->stock_id(); + my $female_type_id = $female_rs->type_id(); + + my $female_accession_name; + my $female_accession_stock_id; + if ($female_type_id == $plot_stock_type_id) { + $female_accession_stock_id = $schema->resultset("Stock::StockRelationship")->find({subject_id=>$female_stock_id, type_id=>$plot_of_type_id})->object_id(); + $female_accession_name = $schema->resultset("Stock::Stock")->find({stock_id => $female_accession_stock_id})->uniquename(); + my $female_plot_individual = Bio::GeneticRelationships::Individual->new(name => $female_parent); + $pedigree->set_female_plot($female_plot_individual); + } elsif ($female_type_id == $plant_stock_type_id) { + $female_accession_stock_id = $schema->resultset("Stock::StockRelationship")->find({subject_id=>$female_stock_id, type_id=>$plant_of_type_id})->object_id(); + $female_accession_name = $schema->resultset("Stock::Stock")->find({stock_id => $female_accession_stock_id})->uniquename(); + my $female_plant_individual = Bio::GeneticRelationships::Individual->new(name => $female_parent); + $pedigree->set_female_plant($female_plant_individual); + } else { + $female_accession_name = $female_parent; + } + + my $female_parent_individual = Bio::GeneticRelationships::Individual->new(name => $female_accession_name); + $pedigree->set_female_parent($female_parent_individual); + + if ($male_parent) { + my $male_accession_stock_id; + my $male_accession_name; + my $male_rs = $schema->resultset("Stock::Stock")->find({uniquename => $male_parent}); + my $male_stock_id = $male_rs->stock_id(); + my $male_type_id = $male_rs->type_id(); + + if ($male_type_id == $plot_stock_type_id) { + $male_accession_stock_id = $schema->resultset("Stock::StockRelationship")->find({subject_id=>$male_stock_id, type_id=>$plot_of_type_id})->object_id(); + $male_accession_name = $schema->resultset("Stock::Stock")->find({stock_id => $male_accession_stock_id})->uniquename(); + my $male_plot_individual = Bio::GeneticRelationships::Individual->new(name => $male_parent); + $pedigree->set_male_plot($male_plot_individual); + } elsif ($male_type_id == $plant_stock_type_id) { + $male_accession_stock_id = $schema->resultset("Stock::StockRelationship")->find({subject_id=>$male_stock_id, type_id=>$plant_of_type_id})->object_id(); + $male_accession_name = $schema->resultset("Stock::Stock")->find({stock_id => $male_accession_stock_id})->uniquename(); + my $male_plant_individual = Bio::GeneticRelationships::Individual->new(name => $male_parent); + $pedigree->set_male_plant($male_plant_individual); + } else { + $male_accession_name = $male_parent + } + + my $male_parent_individual = Bio::GeneticRelationships::Individual->new(name => $male_accession_name); + $pedigree->set_male_parent($male_parent_individual); + } + + push @pedigrees, $pedigree; + + foreach my $additional_info (@$cross_additional_info_headers) { + if ($row->{$additional_info}) { + $cross_additional_info_hash{$cross_name}{$additional_info} = $row->{$additional_info}; + } + } + } + + $parsed_result{'additional_info'} = \%cross_additional_info_hash; + + $parsed_result{'crosses'} = \@pedigrees; + + $self->_set_parsed_data(\%parsed_result); + + return 1; + +} + + +1; diff --git a/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesSimpleExcel.pm b/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesSimpleExcel.pm index db19559f0e..231ba332c7 100644 --- a/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesSimpleExcel.pm +++ b/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesSimpleExcel.pm @@ -8,6 +8,8 @@ use SGN::Model::Cvterm; use Data::Dumper; use CXGN::List::Validate; +# DEPRECATED: This plugin has been replaced by the CrossesGeneric plugin + sub _validate_with_plugin { my $self = shift; my $filename = $self->get_filename(); @@ -170,7 +172,7 @@ sub _validate_with_plugin { $female_parent =~ s/^\s+|\s+$//g; $male_parent =~ s/^\s+|\s+$//g; - + #cross name must not be blank if (!$cross_name || $cross_name eq '') { push @error_messages, "Cell A$row_name: cross unique id missing"; @@ -188,9 +190,9 @@ sub _validate_with_plugin { push @error_messages, "For double haploid, dihaploid_induction, and self, female parent needs to be identical to male parent in row $row_name"; } } - - + + #cross type must not be blank if (!$cross_type || $cross_type eq '') { push @error_messages, "Cell C$row_name: cross type missing"; @@ -214,7 +216,7 @@ sub _validate_with_plugin { $cross_name =~ s/^\s+|\s+$//g; $seen_cross_names{$cross_name}++; } - + if (($cross_type eq 'bulk') || ($cross_type eq 'bulk_self') || ($cross_type eq 'bulk_open')) { #$female_parent =~ s/^\s+|\s+$//g; diff --git a/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesSimplifiedParentInfoExcel.pm b/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesSimplifiedParentInfoExcel.pm index 2b91a4171e..c2a28e7bce 100644 --- a/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesSimplifiedParentInfoExcel.pm +++ b/lib/CXGN/Pedigree/ParseUpload/Plugin/CrossesSimplifiedParentInfoExcel.pm @@ -8,6 +8,8 @@ use SGN::Model::Cvterm; use Data::Dumper; use CXGN::List::Validate; +# DEPRECATED: This plugin has been replaced by the CrossesGeneric plugin + sub _validate_with_plugin { my $self = shift; my $filename = $self->get_filename(); diff --git a/lib/CXGN/Pedigree/ParseUpload/Plugin/IntercrossCSV.pm b/lib/CXGN/Pedigree/ParseUpload/Plugin/IntercrossCSV.pm index 3982268097..cdc7f82779 100644 --- a/lib/CXGN/Pedigree/ParseUpload/Plugin/IntercrossCSV.pm +++ b/lib/CXGN/Pedigree/ParseUpload/Plugin/IntercrossCSV.pm @@ -115,10 +115,10 @@ sub _validate_with_plugin { my @parent_list = keys %parent_names; my $parent_validator = CXGN::List::Validate->new(); - my @parents_missing = @{$parent_validator->validate($schema,'accessions_or_populations_or_plots_or_plants',\@parent_list)->{'missing'}}; + my @parents_missing = @{$parent_validator->validate($schema,'accessions_or_populations_or_crosses_or_plots_or_plants',\@parent_list)->{'missing'}}; if (scalar(@parents_missing) > 0) { - push @error_messages, "The following parents are not in the database, or are not in the database as accession names, plot names or plant names: ".join(',',@parents_missing); + push @error_messages, "The following parents are not in the database, or are not in the database as accession names, population names, cross unique ids, plot names or plant names: ".join(',',@parents_missing); } if (scalar(@error_messages) >= 1) { diff --git a/lib/CXGN/People/Roles.pm b/lib/CXGN/People/Roles.pm index beaa2d6143..5db06cd522 100644 --- a/lib/CXGN/People/Roles.pm +++ b/lib/CXGN/People/Roles.pm @@ -24,6 +24,7 @@ use Try::Tiny; use SGN::Model::Cvterm; use Data::Dumper; use Text::Unidecode; +use List::MoreUtils qw /any /; has 'bcs_schema' => ( isa => 'Bio::Chado::Schema', @@ -83,8 +84,8 @@ sub get_breeding_program_roles { my $dbh = $self->bcs_schema->storage->dbh; my @breeding_program_roles; my $q="SELECT username, sp_person_id, name, censor FROM sgn_people.sp_person - JOIN sgn_people.sp_person_roles using(sp_person_id) - JOIN sgn_people.sp_roles using(sp_role_id) + JOIN sgn_people.sp_person_roles using(sp_person_id) + JOIN sgn_people.sp_roles using(sp_role_id) where disabled IS NULL and sp_person.censor = 0"; my $sth = $dbh->prepare($q); $sth->execute(); @@ -136,4 +137,24 @@ sub get_sp_roles { return \@sp_roles; } +sub check_sp_roles { + my $self = shift; + my $user_roles = shift; + my $program_name = shift; + my %check_roles; + if (!any { $_ eq "curator" || $_ eq "submitter" } (@$user_roles)){ + $check_roles{'invalid_role'} = 1; + } + + my %has_roles = (); + map { $has_roles{$_} = 1; } @$user_roles; + + if (! ( (exists($has_roles{$program_name}) && exists($has_roles{submitter})) || exists($has_roles{curator}))) { + $check_roles{'invalid_program'} = 1; + } + + return \%check_roles; +} + + 1; diff --git a/lib/CXGN/Stock/RelatedStocks.pm b/lib/CXGN/Stock/RelatedStocks.pm index 5272faa8eb..01c45242ce 100644 --- a/lib/CXGN/Stock/RelatedStocks.pm +++ b/lib/CXGN/Stock/RelatedStocks.pm @@ -5,16 +5,25 @@ use strict; use warnings; use Moose; use SGN::Model::Cvterm; +use Data::Dumper; -has 'dbic_schema' => (isa => 'Bio::Chado::Schema', - is => 'rw', - required => 1, +has 'dbic_schema' => ( + isa => 'Bio::Chado::Schema', + is => 'rw', + required => 1, ); -has 'stock_id' => (isa => 'Maybe[Int]', - is => 'rw', +has 'stock_id' => ( + isa => 'Maybe[Int]', + is => 'rw', ); +has 'trial_id' => ( + isa => 'Maybe[Int]', + is => 'rw', +); + + sub get_trial_related_stock { my $self = shift; @@ -250,5 +259,35 @@ sub get_vector_related_stocks { } +sub get_plots_and_plants { + my $self = shift; + my $stock_id = $self->stock_id; + my $trial_id = $self->trial_id; + my $schema = $self->dbic_schema(); + my $plot_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot', 'stock_type')->cvterm_id(); + my $plant_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant', 'stock_type')->cvterm_id(); + my $field_layout_typeid = SGN::Model::Cvterm->get_cvterm_row($schema, "field_layout", "experiment_type")->cvterm_id(); + + my $q = "SELECT stock.stock_id, stock.uniquename + FROM nd_experiment_project join nd_experiment on (nd_experiment_project.nd_experiment_id=nd_experiment.nd_experiment_id) AND nd_experiment.type_id= ? + JOIN nd_experiment_stock ON (nd_experiment.nd_experiment_id=nd_experiment_stock.nd_experiment_id) + JOIN stock_relationship on (nd_experiment_stock.stock_id = stock_relationship.subject_id) AND stock_relationship.object_id = ? + JOIN stock on (stock_relationship.subject_id = stock.stock_id) AND stock.type_id IN (?,?) + WHERE nd_experiment_project.project_id= ? "; + + my $h = $schema->storage->dbh->prepare($q); + + $h->execute($field_layout_typeid, $stock_id, $plot_type_id, $plant_type_id, $trial_id, ); + + my @related_stocks = (); + while(my ($stock_id, $stock_name) = $h->fetchrow_array()){ + push @related_stocks, [$stock_id, $stock_name]; + } + + return\@related_stocks; +} + + + 1; diff --git a/lib/SGN/Controller/AJAX/BreedersToolbox.pm b/lib/SGN/Controller/AJAX/BreedersToolbox.pm index 756dcf1612..c341cb18ad 100644 --- a/lib/SGN/Controller/AJAX/BreedersToolbox.pm +++ b/lib/SGN/Controller/AJAX/BreedersToolbox.pm @@ -18,6 +18,7 @@ use Try::Tiny; use CXGN::Tools::Run; use CXGN::Dataset; use CXGN::Dataset::File; +use CXGN::Stock::RelatedStocks; BEGIN { extends 'Catalyst::Controller::REST' } @@ -94,7 +95,6 @@ sub get_breeding_programs_by_trial :Path('/breeders/programs_by_trial/') Args(1) my $projects = $p->get_breeding_programs_by_trial($trial_id); $c->stash->{rest} = { projects => $projects }; - } sub add_data_agreement :Path('/breeders/trial/add/data_agreement') Args(0) { @@ -248,7 +248,7 @@ sub get_all_trial_types : Path('/ajax/breeders/trial/alltypes') Args(0) { } -sub get_accession_plots :Path('/ajax/breeders/get_accession_plots') Args(0) { +sub get_accession_plots_plants :Path('/ajax/breeders/get_accession_plots_plants') Args(0) { my $self = shift; my $c = shift; my $field_trial = $c->req->param("field_trial"); @@ -256,32 +256,20 @@ sub get_accession_plots :Path('/ajax/breeders/get_accession_plots') Args(0) { my $sp_person_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef; my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado', $sp_person_id); - my $field_layout_typeid = $c->model("Cvterm")->get_cvterm_row($schema, "field_layout", "experiment_type")->cvterm_id(); - my $dbh = $schema->storage->dbh(); my $trial = $schema->resultset("Project::Project")->find ({name => $field_trial}); my $trial_id = $trial->project_id(); - my $cross_accession = $schema->resultset("Stock::Stock")->find ({uniquename => $parent_accession}); - my $cross_accession_id = $cross_accession->stock_id(); - - my $q = "SELECT stock.stock_id, stock.uniquename - FROM nd_experiment_project join nd_experiment on (nd_experiment_project.nd_experiment_id=nd_experiment.nd_experiment_id) AND nd_experiment.type_id= ? - JOIN nd_experiment_stock ON (nd_experiment.nd_experiment_id=nd_experiment_stock.nd_experiment_id) - JOIN stock_relationship on (nd_experiment_stock.stock_id = stock_relationship.subject_id) AND stock_relationship.object_id = ? - JOIN stock on (stock_relationship.subject_id = stock.stock_id) - WHERE nd_experiment_project.project_id= ? "; + my $accession = $schema->resultset("Stock::Stock")->find ({uniquename => $parent_accession}); + my $accession_id = $accession->stock_id(); - my $h = $dbh->prepare($q); - $h->execute($field_layout_typeid, $cross_accession_id, $trial_id, ); - - my @plots=(); - while(my ($plot_id, $plot_name) = $h->fetchrow_array()){ + my $accession_related_stocks = CXGN::Stock::RelatedStocks->new({dbic_schema => $schema, stock_id =>$accession_id, trial_id => $trial_id}); + my $plots_and_plants = $accession_related_stocks->get_plots_and_plants(); + my @results = @$plots_and_plants; + my @blank = ('' , 'Please select a plot or plant'); + unshift @results, [@blank]; - push @plots, [$plot_id, $plot_name]; - } - #print STDERR Dumper \@plots; - $c->stash->{rest} = {data=>\@plots}; + $c->stash->{rest} = {data => \@results}; } diff --git a/lib/SGN/Controller/AJAX/BreedersToolbox/Population.pm b/lib/SGN/Controller/AJAX/BreedersToolbox/Population.pm index 9db69d5b70..438cbb1401 100644 --- a/lib/SGN/Controller/AJAX/BreedersToolbox/Population.pm +++ b/lib/SGN/Controller/AJAX/BreedersToolbox/Population.pm @@ -56,24 +56,26 @@ sub create_population :Path('/ajax/population/new') Args(0) { my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $population_name = $c->req->param('population_name'); - my $accession_list_id = $c->req->param('accession_list_id'); + my $member_type = $c->req->param('member_type'); + my $list_id = $c->req->param('list_id'); + my $members; - if ($accession_list_id){ + if ($list_id){ my $dbh = $c->dbc->dbh; - my $list = CXGN::List->new({dbh=>$dbh, list_id=>$accession_list_id}); + my $list = CXGN::List->new({dbh=>$dbh, list_id=>$list_id}); $members = $list->elements(); } else { my @input_members = $c->req->param('accessions[]'); $members = \@input_members; } - my $population_add = CXGN::Pedigree::AddPopulations->new({ schema => $schema, phenome_schema => $phenome_schema, user_id => $user_id, name => $population_name, members => $members} ); + my $population_add = CXGN::Pedigree::AddPopulations->new({ schema => $schema, phenome_schema => $phenome_schema, user_id => $user_id, name => $population_name, members => $members, member_type => $member_type} ); my $return = $population_add->add_population(); $c->stash->{rest} = $return; } -sub add_accessions_to_population :Path('/ajax/population/add_accessions') Args(0) { +sub add_members_to_population :Path('/ajax/population/add_members') Args(0) { my $self = shift; my $c = shift; my $session_id = $c->req->param("sgn_session_id"); @@ -104,15 +106,16 @@ sub add_accessions_to_population :Path('/ajax/population/add_accessions') Args(0 } my $population_name = $c->req->param('population_name'); - my $accession_list_id = $c->req->param('accession_list_id'); + my $list_id = $c->req->param('list_id'); my $sp_person_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef; my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id); + my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema"); my $dbh = $c->dbc->dbh; - my $list = CXGN::List->new({dbh=>$dbh, list_id=>$accession_list_id}); + my $list = CXGN::List->new({dbh=>$dbh, list_id=>$list_id}); my $members = $list->elements(); - my $population_add = CXGN::Pedigree::AddPopulations->new({ schema => $schema, name => $population_name, members => $members }); - my $return = $population_add->add_accessions(); + my $population_add = CXGN::Pedigree::AddPopulations->new({ schema => $schema, phenome_schema => $phenome_schema, name => $population_name, members => $members, user_id => $user_id }); + my $return = $population_add->add_members(); $c->stash->{rest} = $return; } diff --git a/lib/SGN/Controller/AJAX/Cross.pm b/lib/SGN/Controller/AJAX/Cross.pm index f8474a8112..9021a77090 100644 --- a/lib/SGN/Controller/AJAX/Cross.pm +++ b/lib/SGN/Controller/AJAX/Cross.pm @@ -82,30 +82,14 @@ sub upload_cross_file_POST : Args(0) { } elsif ($experiment_page_crossing_experiment_id) { $crossing_trial_id = $experiment_page_crossing_experiment_id; } - my $crosses_simple_upload = $c->req->upload('xls_crosses_simple_file'); - my $crosses_plots_upload = $c->req->upload('xls_crosses_plots_file'); - my $crosses_plants_upload = $c->req->upload('xls_crosses_plants_file'); - my $crosses_simplified_parents_upload = $c->req->upload('xls_crosses_simplified_parents_file'); + my $crosses_upload = $c->req->upload('upload_crosses_file'); my $upload; my $upload_type; - if ($crosses_plots_upload) { - $upload = $crosses_plots_upload; - $upload_type = 'CrossesExcelFormat'; - } - if ($crosses_plants_upload) { - $upload = $crosses_plants_upload; - $upload_type = 'CrossesExcelFormat'; - } - - if ($crosses_simple_upload) { - $upload = $crosses_simple_upload; - $upload_type = 'CrossesSimpleExcel'; - } - if ($crosses_simplified_parents_upload) { - $upload = $crosses_simplified_parents_upload; - $upload_type = 'CrossesSimplifiedParentInfoExcel'; + if ($crosses_upload) { + $upload = $crosses_upload; + $upload_type = 'CrossesGeneric'; } my $parser; @@ -274,12 +258,11 @@ sub add_cross_POST :Args(0) { my $cross_name = $c->req->param('cross_name'); my $cross_type = $c->req->param('cross_type'); my $crossing_trial_id = $c->req->param('crossing_trial_id'); - my $female_plot_id = $c->req->param('female_plot'); - my $male_plot_id = $c->req->param('male_plot'); + my $female_plot_plant_id = $c->req->param('female_plot_plant'); + my $male_plot_plant_id = $c->req->param('male_plot_plant'); my $cross_combination = $c->req->param('cross_combination'); $cross_name =~ s/^\s+|\s+$//g; #trim whitespace from front and end. - print STDERR "CROSS COMBINATION=".Dumper($cross_combination)."\n"; my $user_id; if (!$c->user()) { print STDERR "User not logged in... not adding a cross.\n"; @@ -308,7 +291,7 @@ sub add_cross_POST :Args(0) { my $maternal = $maternal_parents[$i]; my $polycross_name = $cross_name . '_' . $maternal; print STDERR "First polycross to add is $polycross_name with amternal $maternal and paternal $paternal\n"; - my $success = $self->add_individual_cross($c, $chado_schema, $polycross_name, $cross_type, $crossing_trial_id, $female_plot_id, $male_plot_id, $maternal, $paternal); + my $success = $self->add_individual_cross($c, $chado_schema, $polycross_name, $cross_type, $crossing_trial_id, $female_plot_plant_id, $male_plot_plant_id, $maternal, $paternal); if (!$success) { return; } @@ -326,7 +309,7 @@ sub add_cross_POST :Args(0) { next; } my $reciprocal_cross_name = $cross_name . '_' . $maternal . 'x' . $paternal . '_reciprocalcross'; - my $success = $self->add_individual_cross($c, $chado_schema, $reciprocal_cross_name, $cross_type, $crossing_trial_id, $female_plot_id, $male_plot_id, $maternal, $paternal); + my $success = $self->add_individual_cross($c, $chado_schema, $reciprocal_cross_name, $cross_type, $crossing_trial_id, $female_plot_plant_id, $male_plot_plant_id, $maternal, $paternal); if (!$success) { return; } @@ -341,7 +324,7 @@ sub add_cross_POST :Args(0) { my $maternal = $maternal_parents[$i]; my $paternal = $paternal_parents[$i]; my $multicross_name = $cross_name . '_' . $maternal . 'x' . $paternal . '_multicross'; - my $success = $self->add_individual_cross($c, $chado_schema, $multicross_name, $cross_type, $crossing_trial_id, $female_plot_id, $male_plot_id, $maternal, $paternal); + my $success = $self->add_individual_cross($c, $chado_schema, $multicross_name, $cross_type, $crossing_trial_id, $female_plot_plant_id, $male_plot_plant_id, $maternal, $paternal); if (!$success) { return; } @@ -350,7 +333,7 @@ sub add_cross_POST :Args(0) { else { my $maternal = $c->req->param('maternal'); my $paternal = $c->req->param('paternal'); - my $success = $self->add_individual_cross($c, $chado_schema, $cross_name, $cross_type, $crossing_trial_id, $female_plot_id, $male_plot_id, $maternal, $paternal, $cross_combination); + my $success = $self->add_individual_cross($c, $chado_schema, $cross_name, $cross_type, $crossing_trial_id, $female_plot_plant_id, $male_plot_plant_id, $maternal, $paternal, $cross_combination); if (!$success) { return; } @@ -755,10 +738,12 @@ sub add_individual_cross { my $cross_name = shift; my $cross_type = shift; my $crossing_trial_id = shift; - my $female_plot_id = shift; + my $female_plot_plant_id = shift; my $female_plot; - my $male_plot_id = shift; + my $female_plant; + my $male_plot_plant_id = shift; my $male_plot; + my $male_plant; my $maternal = shift; my $paternal = shift; my $cross_combination = shift; @@ -775,16 +760,29 @@ sub add_individual_cross { my $progeny_number = $c->req->param('progeny_number'); my $visible_to_role = $c->req->param('visible_to_role'); - if ($female_plot_id){ - my $female_plot_rs = $chado_schema->resultset("Stock::Stock")->find({stock_id => $female_plot_id}); - $female_plot = $female_plot_rs->name(); - } + my $plot_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'plot', 'stock_type')->cvterm_id(); + my $plant_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'plant', 'stock_type')->cvterm_id(); + - if ($male_plot_id){ - my $male_plot_rs = $chado_schema->resultset("Stock::Stock")->find({stock_id => $male_plot_id}); - $male_plot = $male_plot_rs->name(); + if ($female_plot_plant_id){ + my $female_plot_plant_rs = $chado_schema->resultset("Stock::Stock")->find({stock_id => $female_plot_plant_id}); + my $female_type = $female_plot_plant_rs->type_id(); + if ($female_type == $plot_cvterm_id) { + $female_plot = $female_plot_plant_rs->name(); + } elsif ($female_type == $plant_cvterm_id) { + $female_plant = $female_plot_plant_rs->name(); + } } + if ($male_plot_plant_id){ + my $male_plot_plant_rs = $chado_schema->resultset("Stock::Stock")->find({stock_id => $male_plot_plant_id}); + my $male_type = $male_plot_plant_rs->type_id(); + if ($male_type == $plot_cvterm_id) { + $male_plot = $male_plot_plant_rs->name(); + } elsif ($male_type == $plant_cvterm_id) { + $male_plant = $male_plot_plant_rs->name(); + } + } #check that progeny number is an integer less than maximum allowed my $maximum_progeny_number = 999; #higher numbers break cross name convention @@ -852,6 +850,16 @@ sub add_individual_cross { $cross_to_add->set_male_plot($male_plot_individual); } + if ($female_plant) { + my $female_plant_individual = Bio::GeneticRelationships::Individual->new(name => $female_plant); + $cross_to_add->set_female_plant($female_plant_individual); + } + + if ($male_plant) { + my $male_plant_individual = Bio::GeneticRelationships::Individual->new(name => $male_plant); + $cross_to_add->set_male_plant($male_plant_individual); + } + $cross_to_add->set_cross_type($cross_type); $cross_to_add->set_name($cross_name); $cross_to_add->set_cross_combination($cross_combination); @@ -920,6 +928,7 @@ sub add_crossingtrial_POST :Args(0){ my $dbh = $c->dbc->dbh; my $crossingtrial_name = $c->req->param('crossingtrial_name'); my $breeding_program_id = $c->req->param('crossingtrial_program_id'); + my $program_name = $schema->resultset('Project::Project')->find({project_id => $breeding_program_id})->name(); my $location = $c->req->param('crossingtrial_location'); my $year = $c->req->param('year'); my $project_description = $c->req->param('project_description'); @@ -930,10 +939,15 @@ sub add_crossingtrial_POST :Args(0){ return; } - if (!any { $_ eq "curator" || $_ eq "submitter" } ($c->user()->roles)){ - print STDERR "User does not have sufficient privileges.\n"; + my @user_roles = $c->user->roles(); + my $check_roles = CXGN::People::Roles->new({bcs_schema => $schema}); + my $invalid_roles = $check_roles->check_sp_roles(\@user_roles, $program_name); + if ($invalid_roles->{'invalid_role'}) { $c->stash->{rest} = {error => "you have insufficient privileges to add a crossing experiment." }; return; + } elsif ($invalid_roles->{'invalid_program'}) { + $c->stash->{rest} = { error => "You need to be either a curator, or a submitter associated with breeding program $program_name to add new crossing experiment." }; + return; } my $user_id = $c->user()->get_object()->get_sp_person_id(); diff --git a/lib/SGN/Controller/AJAX/Trials.pm b/lib/SGN/Controller/AJAX/Trials.pm index 1454255c73..e746235e6e 100644 --- a/lib/SGN/Controller/AJAX/Trials.pm +++ b/lib/SGN/Controller/AJAX/Trials.pm @@ -126,7 +126,7 @@ sub trial_autocomplete_GET :Args(0) { my $sp_person_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef; my $trial_design_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id), "design", "project_property")->cvterm_id(); my @response_list; - my $q = "select distinct(name) from project join projectprop using(project_id) where project.name ilike ? and projectprop.type_id = ? ORDER BY name"; + my $q = "select distinct(name) from project join projectprop using(project_id) where project.name ilike ? and projectprop.type_id = ? and projectprop.value not in ('genotyping_plate', 'genotype_data_project', 'pcr_genotype_data_project') ORDER BY name"; my $sth = $c->dbc->dbh->prepare($q); $sth->execute('%'.$term.'%', $trial_design_cvterm_id); while (my ($project_name) = $sth->fetchrow_array) { @@ -157,7 +157,8 @@ sub trial_lookup : Path('/ajax/breeders/trial_lookup') Args(0) { { 'name' => $trial_name, 'projectprops.type_id' => $trial_type_id }, { join => 'projectprops' } ); - my $trial_id = $rs->project_id() if $rs; + my $trial_id; + $trial_id = $rs->project_id() if $rs; # Trial not found if ( !$trial_id || $trial_id eq '' ) { diff --git a/mason/breeders_toolbox/cross/add_cross_dialogs.mas b/mason/breeders_toolbox/cross/add_cross_dialogs.mas index bb030f5843..6bc75c4016 100644 --- a/mason/breeders_toolbox/cross/add_cross_dialogs.mas +++ b/mason/breeders_toolbox/cross/add_cross_dialogs.mas @@ -22,7 +22,7 @@ $locations

@@ -112,7 +112,7 @@ $locations - + @@ -193,11 +193,11 @@ $locations


- + @@ -427,7 +450,7 @@ $locations

bulk selfed: A group of plants (usually a related family) that are self pollinated (each individual selfed, not combined pollen).

bulk and open pollinated: A group of plants (usually a related family) that are pollinated by another group of plants or open pollinated (pollen may be from a group with known or unknown members).

doubled haploid: Plants derived from doubling the chromosome number of haploid tissue.

-

dihaploid induction: Plants derived from a chromosome reduction from tetraploid to diploid

+

dihaploid induction: Plants derived from a chromosome reduction from tetraploid to diploid


@@ -474,89 +497,130 @@ jQuery.noConflict(); jQuery(document).ready(function($) { - jQuery("#field_trial").autocomplete({ - source:'/ajax/stock/project_autocomplete', + jQuery('input[id*="_field_trial"]').autocomplete({ + source: '/ajax/trials/trial_autocomplete' }); - $("#search_plots").click(function(e){ + jQuery("[name='search_plots_plants']").click(function(e){ e.preventDefault(); - var maternal_parent; - if ($('#maternal_parent').val() != ''){ - maternal_parent = $('#maternal_parent').val(); + let field_trial; + let maternal_parent; + let paternal_parent; + const crossType = jQuery("#cross_type").val(); + + if (crossType == 'open') { + field_trial = jQuery("#female_field_trial").val(); + maternal_parent = jQuery('#open_maternal_parent').val(); + } else { + field_trial = jQuery("#parents_field_trial").val(); + maternal_parent = jQuery('#maternal_parent').val(); } - if ($('#selfed_parent').val() != ''){ - maternal_parent = $('#selfed_parent').val(); + if (!field_trial) { + alert("Please provide trial name"); + return; } - if ($('#open_maternal_parent').val() != ''){ - maternal_parent = $('#open_maternal_parent').val(); + if (!maternal_parent) { + alert("Please provide female parent name"); + return; } - console.log(maternal_parent); + if ((crossType == 'biparental') || (crossType == 'backcross') || (crossType == 'sib')) { + paternal_parent = jQuery('#paternal_parent').val(); + if (!paternal_parent) { + alert("Please provide male parent name"); + return; + } + } jQuery.ajax({ - url:'/ajax/breeders/get_accession_plots', - data:{'field_trial':$('#field_trial').val(), + url:'/ajax/breeders/get_accession_plots_plants', + data:{'field_trial':field_trial, 'parent_accession':maternal_parent, - }, + }, success: function(response){ - console.log(response); - var html = '"; - console.log(html); - - if(i ==''){ - alert("No female plot"); - return; - } - - jQuery('#female_plot').html(html); - + console.log(response); + + var html; + if (crossType == 'open') { + html = ''; + } + for (var i=0; i" + response.data[i][1] + ""; + } + html = html + ""; + console.log(html); + + if(i ==''){ + alert("No female plot"); + return; + } + + if (jQuery('#open_maternal_parent').val() != ''){ + jQuery('#open_female_plot_plant').html(html); + } else { + jQuery('#female_plot_plant').html(html); + } }, + error:function(response){ - alert("An error occurred getting female plots."); + alert("An error occurred getting female plots or plants."); } }); - var paternal_parent; - - if ($('#paternal_parent').val() != ''){ - paternal_parent = $('#paternal_parent').val(); - } - - if ($('#selfed_parent').val() != ''){ - paternal_parent = $('#selfed_parent').val(); + if (paternal_parent) { + jQuery.ajax({ + url:'/ajax/breeders/get_accession_plots_plants', + data:{'field_trial': field_trial, + 'parent_accession':paternal_parent, + }, + success: function(response){ + console.log(response); + let html = '"; + console.log(html); + + if(i ==''){ + alert("No male plot"); + return; + } + + jQuery('#male_plot_plant').html(html); + }, + }); } + }); - jQuery.ajax({ - url:'/ajax/breeders/get_accession_plots', - data:{'field_trial':$('#field_trial').val(), - 'parent_accession':paternal_parent, - }, - success: function(response){ - console.log(response); - var html = '"; - console.log(html); + jQuery("[name='new_cross_close_modal']").click(function() { + jQuery('#create_cross').modal('hide'); + jQuery('#cross_name').val(''); + jQuery('#dialog_cross_combination').val(''); + jQuery('#cross_type').val(''); + jQuery('#maternal_parent').val(''); + jQuery('#paternal_parent').val(''); + jQuery('#selfed_parent').val(''); + jQuery('#open_maternal_parent').val(''); + jQuery('#open_paternal_population').val(''); + jQuery('#bulk_maternal_population').val(''); + jQuery('#bulk_paternal_parent').val(''); + jQuery('#bulk_selfed_population').val(''); + jQuery('#bulk_open_maternal_population').val(''); + jQuery('#doubled_haploid_parent').val(''); + jQuery('#dihaploid_induction_parent').val(''); + jQuery('#parents_field_trial').val(''); + jQuery('#female_field_trial').val(''); + location.reload(); + }); - if(i ==''){ - alert("No male plot"); - return; - } - jQuery('#male_plot').html(html); - }, - }); - }); }); diff --git a/mason/breeders_toolbox/cross/add_crossing_trial_dialogs.mas b/mason/breeders_toolbox/cross/add_crossing_trial_dialogs.mas index a88d99b599..cd10154ec6 100644 --- a/mason/breeders_toolbox/cross/add_crossing_trial_dialogs.mas +++ b/mason/breeders_toolbox/cross/add_crossing_trial_dialogs.mas @@ -37,7 +37,7 @@ $locations
-
+

@@ -54,7 +54,7 @@ foreach my $program (@$programs) {
-

+

@@ -70,13 +70,13 @@ foreach my $program (@$programs) {
-

+

-

+

diff --git a/mason/breeders_toolbox/cross/upload_crosses_dialogs.mas b/mason/breeders_toolbox/cross/upload_crosses_dialogs.mas index 76f7ff5ed5..f47aa85b2c 100644 --- a/mason/breeders_toolbox/cross/upload_crosses_dialogs.mas +++ b/mason/breeders_toolbox/cross/upload_crosses_dialogs.mas @@ -79,7 +79,6 @@ $trial_name => undef <&| /util/workflow.mas:step, title=> "Upload your crosses" &> <& /page/page_title.mas, title=>"Enter basic information about the crosses and upload your file" &> -
-
+

-
- -
- -
-
-