diff --git a/.gitignore b/.gitignore index e8fea74ec4..cd1cd29659 100644 --- a/.gitignore +++ b/.gitignore @@ -49,5 +49,6 @@ docs/_site/ .DS_Store sgn.iml docs/* -!docs/r_markdown_docs -.idea/ \ No newline at end of file +!docs/r_markdown_docs/ +!docs/r_markdown_docs/** +.idea/ diff --git a/bin/convert_treatment_projects_to_phenotypes.pl b/bin/convert_treatment_projects_to_phenotypes.pl new file mode 100644 index 0000000000..02d9af9baa --- /dev/null +++ b/bin/convert_treatment_projects_to_phenotypes.pl @@ -0,0 +1,324 @@ +=head1 NAME + +convert_treatment_projects_to_phenotypes.pl - a script to take deprecated treatment/field_management_factor projects and turn them into treatment observations. Treatment projects will be deleted, and any treatments that are seen will be added to the experiment_treatment ontology. + +=head1 SYNOPSIS + +perl convert_treatment_projects_to_phenotypes.pl -H dbhost -D dbname -U user -P password -e username -t + +=over 4 + +=item -H + +The host of the database. + +=item -D + +The name of the database. + +=item -U + +The user executing this action (postgres by default) + +=item -P + +The database password. + +=item -e + +The signing user. Must be a user in the database. + +=item -t + +Test mode. Changes not committed. + +=back + +=head1 AUTHOR + +Ryan Preble + +=cut + +use strict; + +use Getopt::Std; +use Pod::Usage; +use Bio::Chado::Schema; +use CXGN::Metadata::Schema; +use CXGN::Phenome::Schema; +use CXGN::People::Person; +use CXGN::DB::InsertDBH; +use Data::Dumper; +use SGN::Model::Cvterm; +use CXGN::Trial; +use CXGN::Phenotypes::StorePhenotypes; +use DateTime; +use Cwd; +use File::Temp qw/tempfile/; +use List::Util qw/max/; + +our ($opt_H, $opt_D, $opt_U, $opt_P, $opt_e, $opt_t); + +if (!$opt_U){ + $opt_U = "postgres"; +} + +getopts('H:D:U:P:e:t') + or pod2usage(); + +my $dsn = 'dbi:Pg:database='.$opt_D.";host=".$opt_H.";port=5432"; +my $schema= Bio::Chado::Schema->connect( $dsn, $opt_U, $opt_P, { + AutoCommit => 0, + RaiseError => 1 +} ); +my $dbh = $schema->storage()->dbh(); +my $metadata_schema = CXGN::Metadata::Schema->connect( + sub { $dbh }, + { on_connect_do => ['SET search_path TO public,metadata;'] } + ); +my $phenome_schema = CXGN::Phenome::Schema->connect( + sub { $dbh }, + { on_connect_do => ['SET search_path TO public,phenome;'] } +); +my $site_basedir = getcwd()."/.."; +my $temp_basedir_key = `cat $site_basedir/sgn.conf $site_basedir/sgn_local.conf | grep tempfiles_subdir`; +my (undef, $temp_basedir) = split(/\s+/, $temp_basedir_key); +$temp_basedir = "$site_basedir/$temp_basedir"; +if (! -d "$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$temp_basedir/delete_nd_experiment_ids/"); +} +my $signing_user_id = CXGN::People::Person->get_person_by_username($dbh, $opt_e); #not the db user, but the name attached as operator of new phenotypes + +#definition of trials view +my $all_trials_q = "SELECT trial.project_id AS trial_id, trial.name as trial_name + FROM (((project breeding_program + JOIN project_relationship ON (((breeding_program.project_id = project_relationship.object_project_id) AND (project_relationship.type_id = ( SELECT cvterm.cvterm_id + FROM cvterm + WHERE ((cvterm.name)::text = 'breeding_program_trial_relationship'::text)))))) + JOIN project trial ON ((project_relationship.subject_project_id = trial.project_id))) + JOIN projectprop ON ((trial.project_id = projectprop.project_id))) + WHERE (NOT (projectprop.type_id IN ( SELECT cvterm.cvterm_id + FROM cvterm + WHERE (((cvterm.name)::text = 'cross'::text) OR ((cvterm.name)::text = 'trial_folder'::text) OR ((cvterm.name)::text = 'folder_for_trials'::text) OR ((cvterm.name)::text = 'folder_for_crosses'::text) OR ((cvterm.name)::text = 'folder_for_genotyping_trials'::text))))) + GROUP BY trial.project_id, trial.name;"; + +#Give a description to all new treatment cvterms +my $update_new_treatment_sql = "UPDATE cvterm + SET definition = \'Legacy treatment from BreedBase before sgn-416.0 release. Binary value for treatment was/was not applied.\' + WHERE cvterm_id IN (SELECT unnest(string_to_array(?, ',')::int[]));"; + +my $relationship_cv = $schema->resultset("Cv::Cv")->find({ name => 'relationship'}); +my $rel_cv_id; +if ($relationship_cv) { + $rel_cv_id = $relationship_cv->cv_id ; +} else { + die "No relationship ontology in DB.\n"; +} +my $variable_relationship = $schema->resultset("Cv::Cvterm")->find({ name => 'VARIABLE_OF' , cv_id => $rel_cv_id }); +my $variable_id; +if ($variable_relationship) { + $variable_id = $variable_relationship->cvterm_id(); +} + +my $experiment_treatment_cv = $schema->resultset("Cv::Cv")->find({ name => 'experiment_treatment'}); +my $experiment_treatment_cv_id; +if ($experiment_treatment_cv) { + $experiment_treatment_cv_id = $experiment_treatment_cv->cv_id ; +} else { + die "No experiment_treatment CV found. Has DB patch been run?\n"; +} +my $legacy_experiment_treatment = $schema->resultset("Cv::Cvterm")->find({ name => 'Legacy experiment treatment' , cv_id => $experiment_treatment_cv_id }); +my $legacy_experiment_treatment_root_id; +if ($legacy_experiment_treatment) { + $legacy_experiment_treatment_root_id = $legacy_experiment_treatment->cvterm_id(); +} else { + die "No legacy EXPERIMENT_TREATMENT root term. Has DB patch been run?\n"; +} + +my $get_db_accessions_sql = "SELECT accession FROM dbxref JOIN db USING (db_id) WHERE db.name='EXPERIMENT_TREATMENT';"; + +my $h = $schema->storage->dbh->prepare($get_db_accessions_sql); +$h->execute(); + +my @accessions; + +while (my $accession = $h->fetchrow_array()) { + push @accessions, int($accession =~ s/^0+//r); +} + +my $accession_start = max(@accessions) + 1; + +$h = $schema->storage->dbh->prepare($all_trials_q); +$h->execute(); + +my %new_treatment_cvterms = (); # name => cvterm_id +my %new_treatment_full_names = (); # name => full name (with ontology) +my $dbxref_id = $accession_start; + +my %trial_treatments = (); # holds the full names of all treatments +my $treatment_values_hash = {}; +my %phenotype_store_stock_list = (); + +my $time = DateTime->now(); +my $timestamp = $time->ymd()."_".$time->hms(); + +while(my ($trial_id, $trial_name) = $h->fetchrow_array()) { + + my $trial = CXGN::Trial->new({ + bcs_schema => $schema, + trial_id => $trial_id + }); + + next if (ref($trial) ne 'CXGN::PhenotypingTrial'); #Skip if this is not a phenotyping trial + + my $treatment_trials = $trial->get_treatment_projects(); + + next if (! @{$treatment_trials}); #skip if there are no treatment trials. Don't waste time getting plots or anything. + + my $parent_observation_units = $trial->get_plots(); #get all plots + + my $has_treatments = 0; + + foreach my $treatment_trial (@{$treatment_trials}) { + + $has_treatments = 1; + + my $treatment_trial_name = $treatment_trial->[1]; + my $treatment_trial_id = $treatment_trial->[0]; + + print STDERR "Found a treatment trial with ID $treatment_trial_id \n"; + + $treatment_trial = CXGN::Trial->new({ + bcs_schema => $schema, + trial_id => $treatment_trial->[0] + }); + + my $observation_units = $treatment_trial->get_plots(); + my %observation_units_lookup = map {$_->[0] => 1} @{$observation_units}; + + my $treatment_name = $treatment_trial_name =~ s/^$trial_name\_//r; + # $treatment_name =~ s/_/ /g; + # $treatment_name =~ s/[^\p{Alpha} ]//g; + $treatment_name = lc($treatment_name); #enforce all lowercase + + my $treatment_id; + my $treatment_full_name; + + if (!exists($new_treatment_cvterms{$treatment_name})) { #if this is a new treatment name, make new db entries and get cvterm ids + my $zeroes = "0" x (7-length($dbxref_id)); + eval { + $treatment_id = $schema->resultset("Cv::Cvterm")->create_with({ + name => $treatment_name, + cv => 'experiment_treatment', + db => 'EXPERIMENT_TREATMENT', + dbxref => "$zeroes"."$dbxref_id" + })->cvterm_id(); + + $new_treatment_cvterms{$treatment_name} = $treatment_id; + $treatment_full_name = "$treatment_name|EXPERIMENT_TREATMENT:$zeroes"."$dbxref_id"; + $new_treatment_full_names{$treatment_name} = $treatment_full_name; + $trial_treatments{$treatment_full_name} = 1; + + $schema->resultset("Cv::CvtermRelationship")->find_or_create({ + object_id => $legacy_experiment_treatment_root_id, + subject_id => $treatment_id, + type_id => $variable_id + }); + }; + if ($@) { + die "An error occurred trying to create a new treatment! $@\n"; + } + $dbxref_id++; + } else { #if not new treatment, get the treatment cvterm_id and full names + $treatment_id = $new_treatment_cvterms{$treatment_name}; + $treatment_full_name = $new_treatment_full_names{$treatment_name}; + $trial_treatments{$treatment_full_name} = 1; + } + + foreach my $obs_unit (@{$parent_observation_units}){ #Construct the phenotype values hash + my $plot_name = $obs_unit->[1]; + my $plot_id = $obs_unit->[0]; + my $treatment_val = exists($observation_units_lookup{$plot_id}) ? 1 : 0; + + my $plot = CXGN::Stock->new({ + schema => $schema, + stock_id => $plot_id + }); + + $treatment_values_hash->{$plot_name}->{$treatment_full_name} = [ + $treatment_val, + $timestamp, + $opt_e, + '', + '' + ]; + + my $plot_contents = $plot->get_child_stocks_flat_list(); #treatment values are inherited by child stocks + + $phenotype_store_stock_list{$plot_name} = 1; + + foreach my $child (@{$plot_contents}) { + next if ($child->{type} eq "accession"); #dont want to assign a phenotype to an accession, that would be bad + next if (! $child->{name}); # skip if we have no name + $treatment_values_hash->{$child->{name}}->{$treatment_full_name} = $treatment_values_hash->{$plot_name}->{$treatment_full_name}; + $phenotype_store_stock_list{$child->{name}} = 1; + } + } + + # $trial->remove_treatment_project($treatment_trial_id); # delete the treatment trial; + } +} + +my $phenotype_metadata = { #make phenotype metadata + archived_file => 'none', + archived_file_type => 'treatment project conversion patch', + operator => $opt_e, + date => $timestamp +}; + +my (undef, $tempfile) = tempfile("$temp_basedir/delete_nd_experiment_ids/fileXXXX"); #tempfile + +my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $temp_basedir, + dbhost => $opt_H, + dbname => $opt_D, + dbuser => $opt_U, + dbpass => $opt_P, + temp_file_nd_experiment_id => $tempfile, + bcs_schema => $schema, + metadata_schema => $metadata_schema, + phenome_schema => $phenome_schema, + user_id => $signing_user_id, + stock_list => [grep {defined($_) && $_ ne ''} keys(%phenotype_store_stock_list)], + trait_list => [keys(%trial_treatments)], + values_hash => $treatment_values_hash, + metadata_hash => $phenotype_metadata +}); + +my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + +if ($verified_warning) { + warn $verified_warning; +} +if ($verified_error) { + die $verified_error; +} + +my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + +if ($stored_phenotype_error) { + die "An error occurred converting treatments: $stored_phenotype_error\n"; +} + +$h = $schema->storage->dbh->prepare($update_new_treatment_sql); +$h->execute(join(",", values(%new_treatment_cvterms))); + +if ($opt_t) { + print STDERR "Test mode. Changes not committed.\n"; + $schema->txn_rollback(); +} else { + $schema->txn_commit(); +} + +1; \ No newline at end of file diff --git a/bin/download_obo.pl b/bin/download_obo.pl index 2ceae88b0c..03105f1b9b 100644 --- a/bin/download_obo.pl +++ b/bin/download_obo.pl @@ -5,7 +5,7 @@ =head1 NAME =head1 SYNOPSIS -perl download_obo.pl -i prefix -H host -D dbname +perl download_obo.pl -i prefix -o out_directory -H host -D dbname -U dbuser -P dbpass =head1 DESCRIPTION @@ -70,7 +70,7 @@ =head1 AUTHOR use strict; -use Getopt::Std; +use Getopt::Long; use Data::Dumper; use Try::Tiny; use DateTime; @@ -79,26 +79,25 @@ =head1 AUTHOR use CXGN::DB::InsertDBH; use File::Slurp; -our ($opt_H, $opt_D, $opt_U, $opt_P, $opt_i); +my ($dbhost, $dbname, $dbuser, $dbpass, $prefix, $outdir); -getopts('H:D:U:P:i:'); +GetOptions( + 'H=s' => \$dbhost, + 'D=s' => \$dbname, + 'U=s' => \$dbuser, + 'P=s' => \$dbpass, + 'i=s' => \$prefix, + 'o=s' => \$outdir +); -my $dbhost = $opt_H; -my $dbname = $opt_D; -my $dbuser = $opt_U; -my $dbpass = $opt_P; -my $prefix = $opt_i; +print STDERR "Beginning obo dump to $outdir...\n"; -my $dbh = CXGN::DB::InsertDBH->new( { dbhost=>$dbhost, - dbname=>$dbname, - dbargs => {AutoCommit => 0, - RaiseError => 1} - } ); +my $dsn = 'dbi:Pg:database='.$dbname.";host=".$dbhost.";port=5432"; print STDERR "Connecting to database...\n"; -my $schema= Bio::Chado::Schema->connect( sub { $dbh->get_actual_dbh() } ); +my $schema = Bio::Chado::Schema->connect($dsn, $dbuser, $dbpass); -my $obo_file = $prefix . ".breedbase.obo"; +my $obo_file = "$outdir/$prefix.breedbase.obo"; #resultset of all cvterms my $cvterm_rs = $schema->resultset("Cv::Cvterm")->search( diff --git a/bin/upload_multiple_trial_design.pl b/bin/upload_multiple_trial_design.pl index 04a423572e..2f26d44aac 100644 --- a/bin/upload_multiple_trial_design.pl +++ b/bin/upload_multiple_trial_design.pl @@ -46,6 +46,8 @@ =head1 AUTHOR use CXGN::Trial::TrialCreate; use CXGN::Contact; use CXGN::TrialStatus; +use File::Temp qw/tempfile/; +use CXGN::Phenotypes::StorePhenotypes; my ( $help, $dbhost, $dbname, $basepath, $dbuser, $dbpass, $infile, $username, $email_address, $ignore_warnings); GetOptions( @@ -134,8 +136,51 @@ =head1 AUTHOR my %all_designs = %{$parsed_data}; my %saved_trials; my $coderef = sub { + + my $phenotime = DateTime->now(); + my $phenotimestamp = $phenotime->ymd()."_".$phenotime->hms(); + my $phenotype_metadata = { + archived_file => $infile, + archived_file_type => 'multi trial upload', + operator => $username, + date => $phenotimestamp + }; + my $temp_basedir_key = `cat $basepath/sgn.conf $basepath/sgn_local.conf | grep tempfiles_subdir`; + my (undef, $temp_basedir) = split(/\s+/, $temp_basedir_key); + $temp_basedir = "$basepath/$temp_basedir"; + if (! -d "$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$temp_basedir/delete_nd_experiment_ids/fileXXXX"); #tempfile + + my @phenostore_stocks; + my %phenostore_traits; + my $phenostore_values = {}; + + my $metadata_schema = CXGN::Metadata::Schema->connect( + sub { $dbh }, + { on_connect_do => ['SET search_path TO public,metadata;'] } + ); + my $phenome_schema = CXGN::Phenome::Schema->connect( + sub { $dbh }, + { on_connect_do => ['SET search_path TO public,phenome;'] } + ); + for my $trial_name ( keys %all_designs ) { my $trial_design = $all_designs{$trial_name}; + if ($trial_design->{'design_details'}{'treatments'}) { #construct treatment hash + foreach my $plot (keys(%{$trial_design->{'design_details'}{'treatments'}})) { + foreach my $treatment (keys(%{$trial_design->{'design_details'}{'treatments'}->{$plot}})) { + push @{$trial_design->{'design_details'}{'treatments'}->{$plot}->{$treatment}}, $phenotimestamp; + push @{$trial_design->{'design_details'}{'treatments'}->{$plot}->{$treatment}}, $username; + push @{$trial_design->{'design_details'}{'treatments'}->{$plot}->{$treatment}}, ''; + push @{$trial_design->{'design_details'}{'treatments'}->{$plot}->{$treatment}}, ''; + $phenostore_traits{$treatment} = 1; + $phenostore_values->{$plot}->{$treatment} = $trial_design->{'design_details'}{'treatments'}->{$plot}->{$treatment}; + } + push @phenostore_stocks, $plot; + } + } my %trial_info_hash = ( chado_schema => $chado_schema, dbh => $dbh, @@ -230,6 +275,38 @@ =head1 AUTHOR } } + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $temp_basedir, + dbhost => $dbhost, + dbname => $dbname, + dbuser => $dbuser, + dbpass => $dbpass, + temp_file_nd_experiment_id => $tempfile, + bcs_schema => $chado_schema, + metadata_schema => $metadata_schema, + phenome_schema => $phenome_schema, + user_id => $sp_person_id, + stock_list => \@phenostore_stocks, + trait_list => [keys(%phenostore_traits)], + values_hash => $phenostore_values, + metadata_hash => $phenotype_metadata + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + push @warnings, $verified_warning; + } + if ($verified_error) { + push @errors, $verified_error + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + push @errors, $verified_error + } + }; try { diff --git a/db/00198/AddManagementRegimeProjectProp.pm b/db/00198/AddManagementRegimeProjectProp.pm new file mode 100644 index 0000000000..ae376e4495 --- /dev/null +++ b/db/00198/AddManagementRegimeProjectProp.pm @@ -0,0 +1,84 @@ +#!/usr/bin/env perl + + +=head1 NAME + +AddFieldManagementRegimeProjectProp.pm + +=head1 SYNOPSIS + +mx-run AddFieldManagementRegimeProjectProp [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 subclass uses L. The parent class uses L + +=head1 AUTHOR + +Ryan Preble + +=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 AddManagementRegimeProjectProp; + +use Moose; +extends 'CXGN::Metadata::Dbpatch'; + +use Bio::Chado::Schema; + +has '+description' => ( default => <<'' ); +Adds a projectprop to store project management data. Replaces field management factors, which were deprecated and separated into treatments and management regimes. + +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"; + + my $terms = { + 'project_property' => + [ + "management_regime", + ], + }; + + foreach my $t (sort keys %$terms){ + foreach (@{$terms->{$t}}){ + $schema->resultset("Cv::Cvterm")->create_with( + { + name => $_, + cv => $t + }); + } + } + print STDERR "Patch complete\n"; +} + + +#### +1; # +#### diff --git a/db/00198/CreateExperimentTreatmentCV.pm b/db/00198/CreateExperimentTreatmentCV.pm new file mode 100644 index 0000000000..b206b976f8 --- /dev/null +++ b/db/00198/CreateExperimentTreatmentCV.pm @@ -0,0 +1,172 @@ +#!/usr/bin/env perl + + +=head1 NAME + +CreateTreatmentCV.pm + +=head1 SYNOPSIS + +mx-run CreateTreatmentCV [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 subclass uses L. The parent class uses L + +=head1 AUTHOR + +Ryan Preble + +=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 CreateExperimentTreatmentCV; + +use Moose; +extends 'CXGN::Metadata::Dbpatch'; + +use Bio::Chado::Schema; +use CXGN::DB::InsertDBH; +use Data::Dumper; +use SGN::Model::Cvterm; +use Cwd; +use File::Temp qw/tempfile/; + +has '+description' => ( default => <<'' ); +Creates a controlled vocabulary for experimental treatments. Paired with an ontology that tracks experimental treatments like traits. + +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 $dbhost = $self->dbhost; + my $dbname = $self->dbname; + my $signing_user = $self->username; + + my $schema = Bio::Chado::Schema->connect( sub { $self->dbh->clone } ); + my $site_basedir = getcwd()."/../.."; + my $dbpass_key = `cat $site_basedir/sgn.conf $site_basedir/sgn_local.conf | grep '^dbpass'`; + my (undef, $dbpass) = split(/\s+/, $dbpass_key); + my $dbuser_key = `cat $site_basedir/sgn.conf $site_basedir/sgn_local.conf | grep '^dbuser'`; + my (undef, $dbuser) = split(/\s+/, $dbuser_key); + + print STDERR "INSERTING CV TERMS...\n"; + + my $check_treatment_cv_exists = "SELECT cv_id FROM cv WHERE name='experiment_treatment'"; + my $check_composed_treatment_cv_exists = "SELECT cv_id FROM cv WHERE name='composed_experiment_treatment'"; + + my $h = $schema->storage->dbh()->prepare($check_treatment_cv_exists); + $h->execute(); + + my $row = $h->fetchrow_array(); + + if (defined($row)) { + print STDERR "Patch already run\n"; + } else { + my $insert_treatment_cv = "INSERT INTO cv (name, definition) + VALUES ('experiment_treatment', 'Experimental treatments applied to some of the stocks in a project. Distinct from management factors/management regimes.'), + ('composed_experiment_treatment', '')"; + + $schema->storage->dbh()->do($insert_treatment_cv); + + my $treatment_cv_id = $schema->resultset("Cv::Cv")->find({ + name => 'experiment_treatment' + })->cv_id(); + + my $composed_treatment_cv_id = $schema->resultset("Cv::Cv")->find({ + name => 'composed_experiment_treatment' + })->cv_id(); + + my $treatment_ontology_cvterm_id = $schema->resultset("Cv::Cvterm")->create_with({ + name => 'experiment_treatment_ontology', + cv => 'composable_cvtypes' + })->cvterm_id(); + + my $composed_treatment_ontology_cvterm_id = $schema->resultset("Cv::Cvterm")->create_with({ + name => 'composed_experiment_treatment_ontology', + cv => 'composable_cvtypes' + })->cvterm_id(); + + $schema->resultset("Cv::Cvprop")->create({ + cv_id => $treatment_cv_id, + type_id => $treatment_ontology_cvterm_id, + rank => 0 + }); + + $schema->resultset("Cv::Cvprop")->create({ + cv_id => $composed_treatment_cv_id, + type_id => $composed_treatment_ontology_cvterm_id, + rank => 0 + }); + + my $experiment_treatment_root_id = $schema->resultset("Cv::Cvterm")->create_with({ + name => 'Experimental treatment ontology', + cv => 'experiment_treatment', + db => 'EXPERIMENT_TREATMENT', + dbxref => '0000000' + })->cvterm_id(); + + my $experiment_treatment_legacy_id = $schema->resultset("Cv::Cvterm")->create_with({ + name => 'Legacy experiment treatment', + cv => 'experiment_treatment', + db => 'EXPERIMENT_TREATMENT', + dbxref => '0000001' + })->cvterm_id(); + + my $composed_experiment_treatment_root_id = $schema->resultset("Cv::Cvterm")->create_with({ + name => 'Composed experimental treatment ontology', + cv => 'composed_experiment_treatment', + db => 'COMP_EXP_TREATMENT', + dbxref => '0000000' + })->cvterm_id(); + + my $relationship_cv = $schema->resultset("Cv::Cv")->find({ name => 'relationship'}); + my $rel_cv_id; + if ($relationship_cv) { + $rel_cv_id = $relationship_cv->cv_id ; + } else { + die "No relationship ontology in DB.\n"; + } + my $isa_relationship = $schema->resultset("Cv::Cvterm")->find({ name => 'is_a' , cv_id => $rel_cv_id }); + my $isa_id; + if ($isa_relationship) { + $isa_id = $isa_relationship->cvterm_id(); + } + + $schema->resultset("Cv::CvtermRelationship")->find_or_create({ + object_id => $experiment_treatment_root_id, + subject_id => $experiment_treatment_legacy_id, + type_id => $isa_id + }); + + system("perl $site_basedir/bin/convert_treatment_projects_to_phenotypes.pl -H $dbhost -D $dbname -U $dbuser -P $dbpass -e $signing_user"); + + } + print STDERR "Patch complete\n"; +} + + +#### +1; # +#### diff --git a/docs/00_breedbase_database_description.md b/docs/00_breedbase_database_description.md deleted file mode 100644 index f2c833db35..0000000000 --- a/docs/00_breedbase_database_description.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "0. Breedbase Database Description" -layout: doc_page ---- - - -* TOC -{:toc} - - -For more database implementation information for developers: https://github.com/solgenomics/sgn/wiki diff --git a/docs/01_basic_website_usage.md b/docs/01_basic_website_usage.md deleted file mode 100644 index 1053105d87..0000000000 --- a/docs/01_basic_website_usage.md +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: "1. Basic Website Usage" -layout: doc_page ---- - - -* TOC -{:toc} - - -1.1. Creating a User Account ---- - -### Verifying first that you do not already have an account - -Before creating an account, please verify first that you don’t already have an account. You can use “Search” menu to check if you already registered as a user. - -In the "Search" menu, selecting the "People" tab and search your name. If nothing is found, proceed with the instructions below. Otherwise, clicking the “Login” button. If you have forgotten your password, you can retrieve it by clicking the "Forgot your password?" link on the login page. - -### Creating a user account - -On the right of the toolbar, clicking on “Login.” It will take you to the login page. On the login page, clicking on the link “sign up for an account.” It will take you to the page below: - - - -Filling in all of the information, then clicking "Create Account." - -After you submit the information, an email will be sent to the provided email address. Checking your email and clicking on the link to activate your account. - -1.2. Managing your Account {#managing-your-account} --------------------------- - -### Login - -To login, clicking the "Login" link in the toolbar on any page and enter your username and password. - -If you have forgotten your password, you can retrieve it by clicking the "Forgot your password?" link on the login page. - -login.png - -### Editing Account Settings - -Account settings can be edited by clicking on the "my profile" link displayed as your user name, on the right of the toolbar. You must login, in order to access and change account settings. - -login1.png - -You can add personal information to your account using the "View or update personal information" link. - -To change your password, username, or your contact email, clicking on “Update account information” link. You must provide your old password before you can make any changes. - -login2.png - -### Changing Your Account Status: From “User” to “Submitter” - -After you create an account, your account has a "user" status. This account has limited privileges. - -Accounts with “user” status are able to: - -- Change personal information -- Post comments on pages -- Post to the forum - -To upgrade your account status to "submitter,” contact the database curators using the "contact" link provided at the footer of each page. Submitter accounts can add data, such as new plots, accessions, phenotype data and images. - -### Submitting Feedback on an SGN Database - -We appreciate your feedback! Feel free to submit any questions or suggestions by using the "Feedback" link provided at the footer of each page. - -1.3. Menu Layout ----------------- - -SGN Database websites have a toolbar on the top of each page with a number of menus for convenient access of major functions. The menus, as pictured below, are “search,” “manage,” “analyze,” and “maps.” The toolbar also provides a quick search, a “log in” button, and a “new user” button. - - - -### Menu Options - -#### Search - -In the Search menu, the options are: - -| Tab | Description | -|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------| -| Wizard | Search different accessions and plots by location, year, trial, and trait data. Can also be used to create lists of different types. | -| Accession and plots | Search accessions and plots using a variety of criteria | -| Trials | Search trials by name, description, breeding program, year, location, and trial type. | -| Markers | Search different markers | -| Images | Search images contained in the SGN database | -| People | Search database users | - -#### Manage - -In the Manage menu, the options are: - -| Tab | Description | -|---------------------|----------------------------------------------------------------------------------------------------| -| Breeding Programs | View, add and delete breeding programs | -| Locations | View, add and delete locations | -| Accessions | Manage and search different accessions | -| Seedlots | Manage and search different seedlots | -| Crosses | Create new crosses in the database | -| Field Trials | Manage field trials. Create trials using different field layouts. | -| Genotyping Plates | Manage genotyping plates. Create 96 or 384 well plates. | -| Phenotyping | Upload phenotyping files from the Tablet Field Book application | -| Field Book App | Manage the field book app data (download files to tablet) | -| Barcodes | Refers to the old barcode system, mainly historical | -| Download | Download information in the database based on lists | - -#### Analyze - -**Clicking on the "Analyze" link will give a full menu of all analysis functions** -In the Analyze menu, the options are: - -| Tab | Description | -| --------------------- | --------------------------------------------------------------------------------------- | -| **Breeder Tools** | | -| Breeder Home | Access breeding functionalities. Lists important and helpful links. | -| Barcode Tools | Manage, create, and download barcodes. Also access barcode tools. | -| Genomic Selection | Can search for traits, start building a GS model, and predict values based on genotypes | -| **Sequence Analysis** | | -| BLAST | Sequence homology search | -| **Other** | | -| Ontology Browser | Browse all recorded ontologies | - -1.4. Working with Lists {#working-with-lists} ------------------------ - -Lists are collections of identifiers that are stored in the database. Lists can be composed of accessions, plots, traits, locations, and trials. Lists are attached to the individual user's account, and can only be created and seen by the user while logged in. SGN databases make heavy use of lists in a number of tools on the website. For example, trials are created using lists of accessions. - -### Creating lists - -Lists can be generated in various ways: - -One way to create a list is by clicking on the "Lists" link located on the toolbar. - - - -To create a new list, enter the name of your new list and then clicking on the “New List” button. The name of the list can be anything, but should be unique and should be something to help you easily identify. - - - -You can find the list that you entered on the “Your Lists” page. To add items to your list, click on the "View" icon to open “List Contents” page. - - - -On the “List Contents” page, enter items that you want to add to the list, then click on “Add” button. - - - -The page will be updated and will display your items in a table at the bottom of the page. It is possible to sort the list if you need. - - - -Select the type of items in your list. To verify that the items that you added to your list are already stored in the database and that you selected a correct type for the items, click on the “Validate” button. - - - -If those items are already in the database, a message will indicate that “This list passed validation” - -list9.png - -Note that a list cannot contain duplicate elements. If a duplicate item is entered, the list manager will inform the user that the element is already in the list and will not add it again. - -Another easy way to create a list is to use the [*Search Wizard*]({{ site.baseurl }}{% link 02_searching_the_database.md %}#search-wizard), which can be accessed from the Search menu. - -### Viewing and editing lists - -Lists can be viewed and edited using the "Lists" link on the toolbar. Clicking on the link will open a window that displays all of your lists, as well as an option to create new lists. - -list8.png - -This page shows all lists that have been created, including those created by using the Search Wizard. You can view and edit your lists by using “Actions” buttons. - -1. Clicking on the "view" icon will open a new window called "List Contents" that allows you to change the list name, the type of the list, add new items, or delete existing items. - -2. Clicking on the “delete” icon will delete your list. **Caution: this action cannot be undone**. - -3. Clicking on the “download” icon will download the contents of your list to your computer. - -4. Clicking on the “make public” icon will make your list available for other users to view and use your list. - -list10.png - - -1.5. User Permissions ---- - -Users are assigned into four different groups to determine the level of access they have on the database. This is to prevent data from being altered of deleted in error. -Users are also assigned under Breeding Programs to restrict data access between different breeding programs. -Curators can go to Manage User Roles to see user permissions and change a user's permission level. diff --git a/docs/02_searching_the_database.md b/docs/02_searching_the_database.md deleted file mode 100644 index 96e92cebc9..0000000000 --- a/docs/02_searching_the_database.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: "2. Searching the database" -layout: doc_page ---- - - -* TOC -{:toc} - - -You can search for information on the database by using the following search options: Wizard, which uses combined criteria specified by users; Accessions and Plots; Trials; Markers; Images; People; FAQ. - -![search1.png]({{'assets/images/image267.png' | relative_url }}) - - -2.1 The Search Wizard {#search-wizard} ---------------------- - -![]({{"assets/images/wizard_interface.png" | relative_url }}) - -### 2.1.1 How the Search Wizard Works - -The search wizard presents a number of select boxes, which are initially empty. You start searching by picking a category of data from the dropdown above the left-most select box. - -Once a category has been picked, the database will retrieve all the options within this category and display them within the first select box. You then select one or more options from the first select box, which activates the second dropdown. - -You can then select a category from the second dropdown, and repeat this same search process through all four dropdowns and select boxes. - -![]({{"assets/images/wizard_interface_selections.png" | relative_url }}) - -- In the example above, the "locations" category was chosen in the first dropdown. The first select box then displayed all the possible locations in the database. The option Ibadan was selected. - -- This activated the second dropdown. The category “years” was chosen in the second dropdown. The second select box then displayed all the years that are linked in the database to the location Ibadan. From that list, the options 2011 and 2012 were selected. - -- This activated the third dropdown. A final category, “accessions”, was chosen in the third dropdown. The third select box was then populated with the 3847 accessions in the database that are linked with the location Ibadan in the years 2011 or 2012. - -In addition to the basic search operations demonstrated above, users can take advantage of two more features: - -**Load Selection from List** startfromlist.png - -- Instead of picking a category in the first dropdown, users can instead populate the first selectbox from a list by scrolling down in the first dropdown to the "Load Selection from List" subheading and selecting a list. This is useful for starting queries with a list of plots, as this category is not among the options in the first dropdown. - -**ANY/MIN/ALL** Toggle any_all.jpg - -- By default, the search wizard combines options within a category using an OR query. In the example above, in the third panel the wizard retrieved accessions associated with the location 'Ibadan' in **ANY** of the years "2011 **OR** 2012" - -- If the user clicked the toggle below the second select box to change it to **ALL** before choosing accessions in the third dropdown, the wizard would instead retrieve accessions associated with the location 'Ibadan' in the years "2011 **AND** 2012". This will be a smaller set of accessions, because any accessions used only in 2011, or only in 2012 will be excluded. - -- A more advanced search could use the **MIN** toggle option. This allows the user to make a query in between an ANY or ALL query, where a minimum number of matches from the selected column will be used as a filter for the next column. The minimum can be provided as either a percentage (%) or an actual count of items (#). In the example above, if the years 2011, 2012, and 2013 were selected in the second column, the user could enter '2' in as the minimum and select '#' as the minimum match type. This would select accessions in the third column that were used in 2 or more of the selected years.
min_details.jpg - -### 2.1.2 How to use retrieved data - -#### Getting more Info - -Any option in the wizard select boxes (except for years) can be clicked to open a page with more details. The new page is opened in a new tab. - -#### Saving to a list - -You can store the highlighted items in any selected box to lists. This is done using the inputs and buttons directly below the select box. **Don’t forget, you must be logged in to work with lists!** - -addcreatelist.png - -- To **add items to an existing list**, first pick an existing list using the "Add to List..." dropdown on the left. Then click the "Add" button. A popup window will confirm the action, and display the number of items added to your existing list. - -- To **store items to a new list**, first type a new list name in the "Create New List..." text input on the left. Then click on the "Create" button. A popup window will confirm the action, and display the number of items added to your new list. - -#### Downloading Data - -You can download trial metadata, phenotypes and genotypes associated with the highlighted items in the wizard select boxes. This is done using the buttons in the download section at the bottom of the page. **Don’t forget, you must be logged in to download data!** - -![]({{"assets/images/wizard_download_options.png" | relative_url }}) - -##### Metadata - -Trial metadata can be downloaded by selecting a subset of trials from the database or based on your search categories. To download, click on "Related Trial Metadata", a dialog will appear. Select download format and click the "Metadata" button to complete your download. - -![]({{"assets/images/wizard_related_metadata_download.png" | relative_url }}) - -##### Phenotypes - -The phenotypes download is quite flexible, and can download a subset of all the trial data in the database based on whichever categories and options you currently have selected. Simply click on the “Related Trial Phenotypes” link, review the options, changing or adding any additional parameters you like, then click ‘Download Phenotypes’. - -![]({{"assets/images/wizard_related_phenotypes_download.png" | relative_url }}) - -##### Genotypes - -The genotype download is more stringent. It requires a minimum of one accession and one genotyping protocol to be selected in the wizard select boxes. The text box in the download section of the page will help track what has been selected. Once clicked, the “Download Genotypes” button will download a genotype file for the selected accessions. - -#### Saving the wizard selections - -As discussed above, the selections of the individual select boxes in the wizard can be saved separately to a list. The lists can be used as inputs in other tools on the site. However, sometimes creating a selection is quite time consuming and restoring the selections from four different lists would be cumbersome too. Therefore, the selections can be saved together in a dataset, and named for later retrieval. This is done in the section "Load/Create Datasets" that is below the first two wizard select boxes. To select an existing dataset, one uses the "Load Dataset" dropdown. A particular dataset can be chosen, and the "Load" button can be clicked to retrieve and display the dataset in the wizard. To create a new dataset using items that are selected in the wizard, one can enter the name of the new dataset in the "Create New Dataset" text box. Once the dataset has been given a name, clicking the "Create" button will save the new dataset. - -![]({{"assets/images/wizard_load_create_dataset.png" | relative_url }}) - -### 2.1.3 Updating the Wizard - -The search wizard uses a copy of the database, or a cache, to return results quickly. If data appears to be missing, it usually means that the cache needs to be updated. Users with submitter privileges or above can do this using the ‘Update Wizard’ button. One can also use the ‘Refresh Lists’ button to update the available lists. - -![]({{"assets/images/wizard_update_list_refresh.png" | relative_url }}) - -This will take just a few seconds in small databases, but may take a few hours to complete in larger databases. - - -2.2 Accessions and Plot Search ------------------------------- - -Accessions and their related materials (cross, plant, plot, population, tissue\_sample, training population) can be searched by using “Search Accessions and Plots” page. On this page, “accession” is the default stock type; however, you can change stock type by selecting an option from the drop-down list. -From this page you can construct detailed queries for stock types. For example, by using the "Usage" section, the "Properties" section, and the "Phenotypes" section you could search for accessions which were diploids used in a specific year and location and were also phenotyped for height. You can also search for accessions based on genetic properties, such as the location of an introgression on a specific chromosome. - -![]({{"assets/images/search_accessions.png" | relative_url }}) - -It is possible to query over any of the available properties, such as "ploidy_level", "country of origin", "introgression_chromosome", etc. - -![]({{"assets/images/search_accessions_properties_search.png" | relative_url }}) - -In the search result table it is possible to select any of the available properties to view. - -![]({{"assets/images/search_accessions_properties_view.png" | relative_url }}) - -At the bottom of the accession search there is a phenotype graphical filtering tool. Here you can filter down accessions based on combinations of trait performance. The filtered down accessions are then able to be saved to a list. - -![]({{"assets/images/search_accessions_graphical_filtering.png" | relative_url }}) - -For information on adding Accessions please see the Managing Accessions help. -For information on how field trial plots, plants, tissue samples, and subplots are added to the database, please see the Managing Field Trials help. - -2.3 Trials Search ------------------ - -Trials on the database can be searched based on trial name, description, breeding program, year, location, trial type, design, planting date, and harvest date. - -![]({{"assets/images/trial_search.png" | relative_url }}) - -2.4 Trait Search ------------------ - -On the Trait Search page (menu item `Search > Traits`), traits in the database can be searched by ID, name, or descripiton. Optionally, a starting list of traits can be selected to filter down results. - -![]({{"assets/images/trait-search-default.png" | relative_url }}) - -Selecting traits in the results of the search allows one to add the selected results to a trait list, or create a new trait list from the select results. - -![]({{"assets/images/trait-search.png" | relative_url }}) - -2.5 Ontology Browser ------------------ - -A more advanced tool for searching for Traits is the ontology browser, available by clicking on Analyze and Ontology Browser. From here you can search ontologies and see the various classifications of terms in a tree display. - -![]({{"assets/images/ontology_browser.png" | relative_url }}) - -The terms which appear in the Trait Search in 2.4 are only variable terms. The ontology browser shows these variables as different from their grouping terms by indicating VARIABLE_OF like in the following screenshot. - -![]({{"assets/images/ontology_browser_variable.png" | relative_url }}) - -2.6 Search Seedlots ------------------ - -Seedlots are different from Accessions in that they represent the physical seed being evaluated in an experiment. Seedlots have things like physical storage locations and seed quantities, which accessions do not. To search for available seedlots you go to Manage and then click Seed Lots. By clicking Search Seedlots, you can specify query information. The results from your search will be in the table below the search form. - -![]({{"assets/images/search_seedlots.png" | relative_url }}) - -![]({{"assets/images/manage_seedlots.png" | relative_url }}) - diff --git a/docs/03_managing_breeding_data/03_01.md b/docs/03_managing_breeding_data/03_01.md deleted file mode 100644 index f91d57e438..0000000000 --- a/docs/03_managing_breeding_data/03_01.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "3.1 Managing Breeding Programs" -layout: doc_page ---- - -New breeding programs can be added by using “Add New Program” button on the “Manage Breeding Programs” page. - -![]({{"assets/images/image72.png" | relative_url }}) - -Clicking on the “Add New Program” button will generate a blank form for you to fill out the name and description of the breeding program that you want to add. After completing the form, click on “Add Breeding Program” button to finish the process. - -![]({{"assets/images/image295.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_02.md b/docs/03_managing_breeding_data/03_02.md deleted file mode 100644 index f04b71a9c5..0000000000 --- a/docs/03_managing_breeding_data/03_02.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "3.2 Managing Locations" -layout: doc_page ---- - -Field locations can be managed using the “**Manage Locations**” page. On this page, locations in the database are organized based on their breeding programs. Each location has a link to trials conducted in that location. To add a new location, click on the “Add Location” button that links to the “Add New Location” form. - -![]({{"assets/images/image141.png" | relative_url }}) - -On the “Add New Location” form, fill out the location name that you want to add. Latitude, longitude, and altitude are optional. Submit the new location by clicking on the “Add Location” button at the bottom right of the form. - -![]({{"assets/images/image187.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_03.md b/docs/03_managing_breeding_data/03_03.md deleted file mode 100644 index e05d33c0e6..0000000000 --- a/docs/03_managing_breeding_data/03_03.md +++ /dev/null @@ -1,143 +0,0 @@ ---- -title: "3.3 Managing Accessions" -layout: doc_page ---- -The “Manage Accession” page provides links for adding new accessions. -You can choose to add accessions into the database by either using a List you have created or by uploading XLS or XLSX file. Both options will be detailed below. -To begin click on the "Add Accessions or Upload Accession Info" link. - -![]({{"assets/images/manage_accessions_add_accessions_link.png" | relative_url }}) - -This will open a dialog allowing you to select either "Using Lists" or "Uploading a File". - -3.3.1 Add Accessions Using A List ---- - -First we will show how to add accessions "Using Lists". - -![]({{"assets/images/manage_accessions_add_accessions_using_lists.png" | relative_url }}) - -Here you select an accession list which you have previously made. If you need to create or edit your list you can do so now by clicking "Manage Lists". -Once you have selected your list you can click "Continue". - -The first dialog which can appear will show the accessions which already exist in the database. - -![]({{"assets/images/manage_accessions_add_accessions_found.png" | relative_url }}) - -Click "Continue". The next dialog which can appear will show accessions which have very similar matches to the accession names you are adding. -In the example below, there are two accession names that are very similar to accession names already in the database. 'TME0419' is very similar to 'TME419', and actually is probably a mistake that should not be added to the database. - -![]({{"assets/images/manage_accessions_add_accessions_fuzzy.png" | relative_url }}) - -To avoid situations in adding a mistaken duplicate accession, the database gives you options for moving forward with these very similar looking accession names. You can either "continue saving the name in your list", "replace name in your list with selected existing name", "remove name in your list and ignore", or "add name in your list as a synonym to selected existing name". - -![]({{"assets/images/manage_accessions_add_accessions_fuzzy_options.png" | relative_url }}) - -Clicking "Download Fuzzy Matches" will return a tabular result of the "fuzzy" accession name results shown. -Click "Make changes and continue" to move on. - -The final dialog shows the accessions that will be added. Here you need to assign the species of these accessions. You can optionally group the accessions into a population and/or add an organization for the accessions. - -![]({{"assets/images/manage_accessions_add_accessions_complete_using_list.png" | relative_url }}) - -Once you click "Add Accessions", the new accessions will be created in the database and you will see the following confirmation dialog, which includes links to the newly created accessions. - -![]({{"assets/images/manage_accessions_add_accessions_saved.png" | relative_url }}) - -3.3.2 Uploading Accessions and Accession's Info From A File ---- - -The process to upload accessions is very similar to using a list, but enables you to add a variety of properties, such as synonyms, to the accessions in bulk. - -![]({{"assets/images/manage_accessions_add_accessions_using_file.png" | relative_url }}) - -Clicking on "Spreadsheet format" will show the following dialog. -Here it shows that the file must be XLS or XLSX format and can contain a number of header columns as attributes. It is important that you use exactly the same header column names as listed here. In columns that indicate that many attribute values can be passed at once using (s), such as synonym(s), you can pass a comma separated list of values, such as 'synonym1,synonym2'. - -![]({{"assets/images/manage_accessions_add_accessions_spreadsheet_info.png" | relative_url }}) - -Once you have selected your XLS or XLSX file for upload, click "Continue". - -The following process is the same way as with lists: - -The first dialog which can appear will show accession names which are already in the database. - -Click "Continue" and the next dialog that can appear will show "fuzzy" matches for the accession names you are trying to upload. Here you can choose to prevent adding accession names which look very similar to each other as wrongly duplicated accessions. - -Click "Continue" and the final dialog that will appear will show the information to be added into the database. Here it is divided into accession names that are new and accession names that already exist in the database; however, for the accession names that already exist it will show additional attributes that originated from your file that will be added to these accessions. - -![]({{"assets/images/manage_accessions_add_accessions_complete_using_file.png" | relative_url }}) - -Once you click "Add Accessions", the new accessions and information will be created in the database and you will see the following confirmation dialog, which includes links to the created and updated accessions. - -![]({{"assets/images/manage_accessions_add_accessions_saved.png" | relative_url }}) - - -3.3.3 Add Parentage (Pedigree) Information to Accessions ---- - -Pedigree data can be uploaded from your computer by clicking on “Upload Pedigree File” - -![]({{"assets/images/image286.png" | relative_url }}) - -***IMPORTANT!* Please use only tab-delimited text file format (.xls or .xlsx formats are NOT supported).** - -You can find detailed information on how to prepare pedigree file by clicking on “File format information” - -The currently supported format has four tab separated columns: - -progeny name female parent accession male parent accession type - -Type can be biparental, self, backcross, sib, polycross, reselected, or open. In the case of the open type, the male parent accession field can remain blank. For all other types, both columns should be filled, even if they contain the same information as another column (such as self). - -![]({{"assets/images/image333.png" | relative_url }}) -![]({{"assets/images/pedigree_upload_format.png" | relative_url }}) - - -3.3.4 Working with grafts ---- - -Grafts are plants that are composed of a rootstock and a scion, which are genetically different and fused together, usually at the stem level. - -To work with grafts, the grafts interface needs to be activated by adding a configuration parameter in the sgn_local.conf file. The parameter is show_grafting_interface. It should be set to 1 in sgn_local.conf, the default is 0 (in sgn.conf). - -Grafts to be created need to be specified using an Excel file (xlsx format) with two columns. The first column should have the header "scion accession" and should list accession names that will be scions. The second column should have the header "rootstock accession" and should list accession names that will be rootstocks. - -In the database, the graft accessions will created as single accessions. The graft accession will have two relationships, one to the scion accession (scion_of relationship) andone to the rootstock (rootstock_of relationship). These relationships are displayed on the pedigree viewer. The graft accession name is created from the scion accession name and the rootstock accession name, separated by the graft separator character. By default, the graft separator character is the plus sign '+'. The graft separator character can be changed in the sgn_local.conf file, using the parameter graft_separator_string. The graft separator string should not occur in any other accession names that are not grafts. - -When the grafting interface is activated, a new button will be shown on the manage accessions page, called "Upload Grafts". - -Clicking the button brings up the upload grafts dialog. - -Select the Excel file containing the grafting information. The system will validate the file, for example, check whether the accessions are in the database, and if the headers are correct. - -The validation result will be presented, and if problems are found, they will be listed. In addition, if there are problems, the Upload button will be grayed out and upload will not be possible. Conversely, if there are no problems, the Upload button will be activated and can be clicked to upload the data. - -If the upload completes, a completion message is displayed with a summary what was uploaded. - -Grafted accessions can be used like any other accession, for example, they can be used on field layouts. If you create a list of graft accessions, use the list type 'accessions'. - -Note that you shouldn't create new grafts based on other grafts. The scion accession and the rootstock accession have to be different, otherwise they will not be created. - -3.3.5 Bulk renaming of accessions ---- - -Accessions can be renamed in bulk using the rename accessions feature. To rename accessions, prepare a tab delimited file with two columns: the first column should have the header "old name" and contain the accession names that need to be changed. The second column should have the header "new name" and contain the names that the accessions in column 1 should be renamed to. - -The accession renaming feature is available from the Manage->Accessions page. Click on the "Rename Accessions" button. The first step is the upload of the file with a verification step. The verification step checks whether all the accession names in column 1 exist in the database, and whether all the accession names given in column 2 do NOT exist in the database. Only if both conditions are met, will the "rename" button become active, otherwise an error message is displayed listing the offending accession names. - -Optionally, the old name can be automatically added as a synonym to the renamed accession, using the checkbox on the submit form. This option is clicked by default. Unclick the checkbox to NOT save any old names as synonyms. - -Note that accession renaming should not be undertaken lightly. This feature is intended for special use cases, such as where accessions are created in a nursery with a name that is different from the accession name in the downstream breeding program. - -It can also be used to rename accessions in bulk that have spelling mistakes and other issues. Please note however, that the tool does not make any attempt to change the names of associated elements, such a plots, that may have been constructed using accession names. - -Because of the many implications of accession renaming, the feature is limited to accounts with the curator role. - - - - - - - - diff --git a/docs/03_managing_breeding_data/03_04.md b/docs/03_managing_breeding_data/03_04.md deleted file mode 100644 index 553389f98b..0000000000 --- a/docs/03_managing_breeding_data/03_04.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: "3.4 Managing Seed Lots" -layout: doc_page ---- - - -* TOC -{:toc} - - -Seedlots are different from Accessions in that they represent the physical seed being evaluated in an experiment. Seedlots have things like physical storage locations and seed quantities, which accessions do not. The seed in seedlots can be from crosses or can be named accessions. Seedlots from crosses would represent seed harvested. Click Manage and then Seed Lots to begin. - -![]({{"assets/images/manage_seedlots.png" | relative_url }}) - -3.4.1 Add New Seedlot(s) ------------------ - -To add a single new seedlot, click on "Add Seedlot". This will bring up the following dialog where you enter information about where the seedlot exists, what accession or cross is contained in it, and how many seeds there are. A seedlot must contain either an accession or a cross, and not both. A seedlot must have a weight in grams or a seed count or both of these. - -![]({{"assets/images/manage_seedlot_add_seedlot.png" | relative_url }}) - -In the case where you have many seedlots to add to the database, you can upload an excel XLS or XLSX file instead. Click "Upload Seedlots" to see the following dialog. - -![]({{"assets/images/manage_seedlot_upload_seedlots.png" | relative_url }}) - -3.4.2 Seedlot Transactions ------------------ - -Seedlots are capable of tracking where seeds came from, such as from crosses, and to where seeds go, such as to plots in the field. If you navigate to a seedlot detail page you will see the following. - -![]({{"assets/images/seedlot_detail_page.png" | relative_url }}) - -On this page you see and can edit information regarding a single seedlot, such as its name and location. You will also see a table indicating all t he transactions that a seedlot has been involved in, such as if it was planted in a plot in the field. Transactions to field plots are created when adding or uploading a new trial or from a trial's detail page. Clicking on "Add New Transaction" let you add a transaction from between this seedlot and another seedlot. This kind of transaction is useful for representing if you have distributed seed to different locations. - -![]({{"assets/images/seedlot_add_new_transaction.png" | relative_url }}) - -3.4.3 Seed Inventory ------------------ - -To inventory your seed: -1) Make sure your seedlots are in the database. Use "Add New Seedlot" to add a single seedlot or "Upload New Seedlots" to add many. -2) Make sure your seedlots are barcoded. You can print these barcodes from the database. -3) Use the "Inventory" Android Application to scan seedlot barcodes and record weight. Then use "Upload Inventory" to upload this info into database. If you prefer you can create your own CSV file and upload that, if you do not want to use the Inventory Application. -For more info about the "Inventory" Android Application go to Inventory. - -Clicking the "Upload Inventory" button will bring the following dialog: - -![]({{"assets/images/manage_seedlot_upload_inventory.png" | relative_url }}) - -The CSV file that should contain your inventory should meet these Template requirements. The Seed Inventory Android Application exports this exact file. - -![]({{"assets/images/manage_seedlot_upload_inventory_template.png" | relative_url }}) - -3.4.4 Find Seedlots For a List of Accessions ------------------ - -A convenient tool for searching available seedlots for a list of accessions is available in the list tool. First open up your list of accessions. For help opening a list of accessions please see the List section help. There is a button called "See Available Seedlots". - -![]({{"assets/images/manage_seedlot_accession_list_search.png" | relative_url }}) - -Once you click this, you will see the following table in a dialog. From here you can create a list of seedlots using the checkboxes and the input at the bottom. - -![]({{"assets/images/manage_seedlot_list_available_seedlots.png" | relative_url }}) - -3.4.5 Create a seedlot for an Accession or Cross ------------------ - -Complementary to what we saw above for creating seedlots from the "Manage Seedlots" page, it is possible to create a new seedlot from an accession's detail page or from the cross detail page. On the accession detail page, this is visible in the "Related Stocks" section as seen below. The cross detail page has an identical section. Notice the link for creating a new seedlot, which streamlines adding the seedlot. - -![]({{"assets/images/manage_seedlot_accession_detail.png" | relative_url }}) - -3.4.6 Add quality data to a seedlot ------------------- - -Quality information can be added to a seedlot in the quality field. This is also available as a column in the file upload format. It is recommended to use a controlled vocabulary, defined by the user, for the quality field. For example, good quality seed should be labelled "ok", whereas other quality descriptors could be "moldy", "insect damage", or "low sprouting", etc. - -3.4.7 Seedlot Maintenance Events ------------------- - -For some crops, such as sugar kelp, a "seedlot" requires routine maintenance for the successful long-term storage of the seedlot. (For example, a Seedlot Maintenance Event for sugar kelp would be the routine change of the water that gametophytes are kept it). Breedbase can now store a record of these Seedlot Maintenance Events associated directly with existing Seedlots. Maintenance Events can be uploaded using a simple Excel template or recorded directly on the website. - -### Setup - -Each Breedbase instance needs to be configured to support the storage of Seedlot Maintenance Events since each crop will have their own distinct set of maintenance events for their seedlots. To check if your Breedbase instance supports this feature, go to the Manage menu and select the Seed Lots page. Make sure you are logged in and look for the **Seedlot Maintenance** button near the top, next to the **Create Seedlot(s)** and **Upload Inventory** buttons. If you don't see this button, contact the developer(s) supporting your Breedbase instance and ask if they can setup this feature. - -![The Manage Seedlots page showing the location of the Seedlot Maintenance button]({{"assets/images/manage_seedlots_seedlot_maintenance.png" | relative_url }}) -*The location of the Seedlot Maintenance button on the Manage > Seed Lots page* - -### Adding Events - -Seedlot Maintenance Events can be added using two methods: 1) Uploading an Excel template or 2) Recording events directly on the website - -#### Uploading Events with Excel Template - -To bulk-upload a file of Seedlot Maintenance Events, first create an Excel (.xls or .xlsx) file with the following headers: - -- **seedlot** - the name of the Seedlot to associate the event with (must exactly match an existing Seedlot in the database) -- **type** - the name of the Seedlot Maintenance Event type (these vary between Breedbase instances, a list of supported event types is displayed on the upload page) -- **value** - the value of the Seedlot Maintenance Event (these may be different for each event type and vary between Breedbase instances, a list of supported event values is displayed on the upload page) -- **notes** - optional, additional notes/comments about the event -- **operator** - the username of the Breedbase user that recorded the event -- **timestamp** - the date/time the event was recorded, in 'YYYY-MM-DD HH:MM:SS' format - -Once you have an Excel file with the events filled out, follow these steps to upload the events to the database: - -1. Make sure you are logged in to your Breedbase instance -2. Go to the Manage > Seed Lots page -3. Select the **Seedlot Maintenance** button -4. Select the **Upload Maintenance** button -5. Choose your Excel (.xls or .xlsx) file to upload -6. Select the **Upload** button - -![The Seedlot Maintenance upload dialog]({{"assets/images/seedlot_maintenance_upload.png" | relative_url }}) -*The Seedlot Maintenance upload dialog, showing the supported event types and values (for sugar kelp)* - -#### Recording Events on Website - -To add individual Seedlot Maintenance Events to the database in real time, as they're being recorded, use the **Record Maintenance** page. Follow these steps to record Seedlot Maintenance Events: - -1. Make sure you are logged in to your Breedbase instance -2. Go to the Manage > Seed Lots page -3. Select the **Seedlot Maintenance** button -4. Select the **Record Maintenance** button -5. Enter the **Seedlot Name** or scan a barcode that has the Seedlot Name encoded. Once entered, the box at the top of the page will display basic information about the Seedlot as well its recently recorded events. -6. Select or Enter the values of individual events -7. Optionally, notes button next to each event to add additional notes/comments about that specific event -8. Make sure the operator/username and timestamp are correct -9. Select the **Submit** button to add the recorded events to the database. NOTE: any events that remain selected as "Not Recorded" will not be submitted to the database. - -![The Seedlot Maintenance record page]({{"assets/images/seedlot_maintenance_record.png" | relative_url }}) -*The Seedlot Maintenance record page, as configured for sugar kelp* - -### Displaying Events - -Recently recorded Seedlot Maintenance Events are displayed in a table from the main Seedlot Maintenance page, as well as the detail page for individual Seedlots. - -![Unfiltered table of recent Seedlot Maintenance events]({{"assets/images/seedlot_maintenance_events_unfiltered.png" | relative_url }}) -*Unfiltered table of recent Seedlot Maintenance events* - -The events displayed in these tables are sorted by timestamp, with the most recently recorded events displayed first. The displayed events can be filtered using any number of supported filter criteria, such as: -- seedlot names (as entered on the page or using an existing seedlot list), -- dates (on, on or before, before, on or after, and/or after the entered dates) -- event types -- event type values -- operator/username - -Select the properties of the filter(s) you want to apply, then select the **Add** button next to the button to add the filter to the list of applied filters. Once you're done adding filters, select the **Filter** button to search the database for the filtered events. - -![A filtered table of Seedlot Maintenance events]({{"assets/images/seedlot_maintenance_events_filtered.png" | relative_url }}) -*A filtered table of Seedlot Maintenance events* - -The filtered events can be downloaded directly from the table using the **Excel** or **CSV** buttons at the top of the table. Or Seedlot Maintenance Events can be bulk-downloaded (this includes all events for a Seedlot) using a list of Seedlots from the main downloads page (see below). - -### Downloading Events - -To bulk-download all events for a specific subset of Seedlots: - -1. Create a list containing the Seelots you are interested in. -2. Go to the **Download Using Lists** page (Manage > Download) -3. Find the **Download Seedlot Maintenance Events** section -4. Select your list of Seedlots -5. Select the **Download** button to generate the download file - -The downloaded file will follow the same format as the upload template and will contain all recorded Seedlot Maintenance Events for each Seedlot in the list. - -## Deleting Seedlots - -Seedlots can be deleted on the Manage Seedlots page (/breeders/seedlots) by search the seedlot and then clicking the X to delete one seedlot at a time. To delete a seedlot, the logged in user needs the required delete privileges on the seedlot. The seedlot also should not have any transactions associated with it (except for the initial transaction). - -To delete seedlots in bulk, generate a list of type seedlot, for example, using the wizard. Open the section "Delete seedlots using a list" on the Manage Seedlots page and select the list. Seedlot deletion using a list is only available to user with curator status. diff --git a/docs/03_managing_breeding_data/03_05.md b/docs/03_managing_breeding_data/03_05.md deleted file mode 100644 index e93c095534..0000000000 --- a/docs/03_managing_breeding_data/03_05.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: "3.5 Managing Populations" -layout: doc_page ---- - -Populations are modeled as groups of accessions. This grouping can be useful in downstream analyses. To manage these populations go to Manage Accessions and scroll tp the bottom. - -![]({{"assets/images/manage_populations.png" | relative_url }}) - -To add a new population click "Create Population". The following dialog will appear where you choose a list of accessions and give a name to the new population. Please note it is also possible to create a population when you are uploading new accessions into the database. - -![]({{"assets/images/manage_populations_add_population.png" | relative_url }}) - -Click on the plus (+) button next to Populations to see all the available populations. Click on a population name to see the accessions in the population. - -![]({{"assets/images/manage_populations_expand_table.png" | relative_url }}) - -From here you can delete accessions from a population as well as add new accessions to the population. - -![]({{"assets/images/manage_populations_table.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_06.md b/docs/03_managing_breeding_data/03_06.md deleted file mode 100644 index 8f953a44d0..0000000000 --- a/docs/03_managing_breeding_data/03_06.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -title: "3.6 Managing Crosses" -layout: doc_page ---- - - -* TOC -{:toc} - - -Information for crosses can be managed using the “Crosses" option in the Manage menu. - -![]({{"assets/images/image148.png" | relative_url }}) - -Crossing Experiment ----------- - -Different crosses in the same trial/nursery/project are grouped via "**crossing experiment**". Crossing experiments are organized based on their breeding programs. To find a crossing experiment, you can either type the crossing experiment name in the “Search” box, or look for the crossing experiment directly in its breeding program by clicking on the “**+**” icon. In each breeding program, crossing experiments can be placed directly in the breeding program, or organized in folders. The "**Folders**" section allows you to place crossing experiments in folders, move a crossing experiment in a folder to another folder, or rearrange your folders within a breeding program. - -![]({{"assets/images/manage_crosses1.png" | relative_url }}) - -![]({{"assets/images/manage_crosses2.png" | relative_url }}) - -### 3.6.1 Add New Crossing Experiment - -To add a new crossing experiment, click on "Add Crossing Experiment" link. - -![]({{"assets/images/manage_crosses_crossingtrial.png" | relative_url }}) - -Required Information: - -• “**Crossing Experiment Name**": enter a name for the crossing experiment. The crossing experiment name must not already exist in the database. - -• “**Breeding program**": select a breeding program that is available in the database. New breeding programs can be added on the "Breeding program" page, accessible from the "Manage" menu. *Breeding Program Page* - -• “**Location**”: select a location for the crossing experiment. New locations can be entered on the "**Locations**" page, accessible from the "**Manage**" menu. *Location Page* - -• “**Year”**: select a year. - -• “**Description**": enter a description for the crossing experiment. - -After filling in the information, click "**Submit**" to generate the crossing experiment. - -![]({{"assets/images/add_crossingtrial.png" | relative_url }}) - -Cross ----------- - -### 3.6.2 Add New Crosses - -#### 3.6.2.1 Add a cross by using the "Add New Cross" dialog - -To add a single new cross, click on "Add Cross" link. - -![]({{"assets/images/manage_crosses_addcross.png" | relative_url }}) - -Enter cross information in the popup dialog. - -![]({{"assets/images/add_cross.png" | relative_url }}) - -Required Information: - -• “**Crossing experiment**": select a crossing experiment available in the database. - -• “**Location**": select a location available in the database. - -• “**Cross name**": enter a name for the cross. The cross name must not already exist in the database. - -• “**Cross type**": the options for cross types are: biparental, self, open pollinated, bulk, bulk selfed, bulk and open pollinated, double haploid, polycross, reciprocal and multicross. - -![]({{"assets/images/new_cross_dialog_type.png" | relative_url }}) - -• The “**Female Parent”** and “**Male Parent”** field are auto-complete fields for accessions that are already in the database. The parents specified will be entered in the pedigree of the new accessions generated by this cross. - -Optional Information: - -• “**Female Plot and/or Male Plot**": In addition to the accession names, specific plots used in the cross can also be added to the database. To retrieve plot names associated with each female/male accession, enter your trial name, then click "**Search Plots**". Plot names of each parental accession in that field trial will be shown in the drop-down list, you can then select the plot used in the cross. - -![]({{"assets/images/new_cross_dialog2.png" | relative_url }}) - -Additional crossing experimental information such as pollination date, number of flowers, number of fruits, number of seeds can be specified during adding new cross. Alternatively, this information can be updated or edited directly on the "**Cross Details**" page. - -If you know the number of accessions that are generated from the cross, they can be instantiated immediately in the database by clicking the "**Add accessions for progeny**" checkbox and specifying the number. - -![]({{"assets/images/add_cross_info.png" | relative_url }}) - -Click "Submit" to generate the cross. - -#### 3.6.2.2 Upload New Crosses - -To upload new crosses from an Excel file (.xls or .xlsx), click on "Upload Crosses" link. - -![]({{"assets/images/manage_crosses_upload.png" | relative_url }}) - -Select a crossing experiment and a location available in the database from drop-down lists and choose a file that you want to upload, then click "**Upload File**". - -![]({{"assets/images/upload_crosses.png" | relative_url }}) - -Please check spreadsheet format carefully. The file must be an Excel file (.xls or .xlsx). - -![]({{"assets/images/cross_upload_format.png" | relative_url }}) - -### 3.6.3 Update Crosses by Uploading - -To upload progenies and/or experimental info of crosses already in the database, go to "**Manage-Upload**" page. - -In the "**Crosses**" section, there are links for uploading progenies and experimental info. - -![]({{"assets/images/cross_upload_page.png" | relative_url }}) - -Please check spreadsheet format in each link carefully. The file must be an Excel file (.xls or .xlsx). - -![]({{"assets/images/progenies_upload_spreadsheet.png" | relative_url }}) - -![]({{"assets/images/crossinfo_upload_spreadsheet.png" | relative_url }}) - -Note: crossing experimental information is customized based on the need for each crop. As a result, column headers for experimental info in your database may be different from the information shown in this manual. - -Cross Wishlist ------------------ - -An Android ODK application is being developed to record cross information on a mobile device in the field. To link this mobile application with the database, the Cross Wishlist can be used to create a plan for which crosses to perform. - -This tool is available on the Manage Cross page. It is currently only available on certain databases, so when you click this link you may see an alert mentioning that the cross wishlist is not available on your database. - -![]({{"assets/images/manage_cross_click_cross_wishlist.png" | relative_url }}) - -### 3.6.4 Create a Cross Wishlist - -#### Step 1. Select the accessions to be crossed in your trial - -There are two interfaces for this step, either "Not Using Lists" or "Using Lists". Depending on if you already have a list of female and male accessions to use, you can decide on which interface to use. The end result of using either interface is the same. - -![]({{"assets/images/cross_wishlist_initial_dialog.png" | relative_url }}) - -We will start by showing "Not Using Lists". First select the trial in which the crosses are to be performed. This will populate a select box with all the accessions used in that trial. From here, one or many accessions can be selected as the female accession. - -![]({{"assets/images/cross_wishlist_not_using_list_01.png" | relative_url }}) - -Once the female accessions are selected, a table is populated. Each row in this table begins with the female accession that was selected, followed by a select box with all the accessions used in the trial. From here, one or many accessions can be selected as the male to use in the cross. - -![]({{"assets/images/cross_wishlist_not_using_list_02.png" | relative_url }}) - -Once the male accessions are selected to cross with each female accession, a table indicating priorities appears. Priority is meant to indicate an order in which to attempt the cross; first the highest priority male will be considered, but if this cross is not possible then subsequent males will be considered. An equal priority can be given and this will not indicate a specific order to follow. - -![]({{"assets/images/cross_wishlist_not_using_list_03.png" | relative_url }}) - -Alternatively, we could have used the "Using List" interface instead. Here we select the trial in which the crosses will be performed and we provide a list of accessions to consider for the females and the males to be crossed. - -![]({{"assets/images/cross_wishlist_using_list_01.png" | relative_url }}) - -#### Step 2. Select the female plots to be considered in the crosses - -After selecting your lists, the table below is populated. The first column has all the female accessions specified and the header row has all the male accessions specified. The males to consider crossing with each female are indicated with priority. - -![]({{"assets/images/cross_wishlist_using_list_02.png" | relative_url }}) - -After female and male accessions are selected to cross, either by the "Nor Using List" or "Using List" interface, click Next. The next dialog will allow selection of specific female plots to use for the cross. Sections for each female accession selected will appear with the field layout displayed. Selecting all plots in which the female is present indicates that the cross should be performed on all plots where that female accession is present. - -![]({{"assets/images/cross_wishlist_not_using_list_04.png" | relative_url }}) - -#### Step 3. Transfer the cross wishlist to your mobile crossing application - -Clicking "Push Cross Wishlst for ODK Use" will send the cross wishlist plan to the ONA server for use by the mobile ODK application. Crosses can then be performed and recorded in the field using the mobile application. Afterwards, the crosses are sent back to our database and stored. - -Crossing Experiment Detail Page ----------- - -Information for crosses in the same crossing experiment is compiled in the crossing experiment detail page. - -![]({{"assets/images/crossingtrial_1.png" | relative_url }}) - -![]({{"assets/images/crossingtrial_2.png" | relative_url }}) - -Each cross name, female parent, male parent, female plot and male plot has a link to its own detail page, which contains information specific to each one. -Note: crossing experimental information is customized based on the need for each crop. As a result, the details of the information in your database may be different from the information shown in this manual. - -Cross Detail Page ----------- - -Information of each cross can also be viewed in its detail page. - -![]({{"assets/images/cross_page_1.png" | relative_url }}) - -![]({{"assets/images/cross_page_2.png" | relative_url }}) - - This page allows you to update or edit crossing experimental information and add progenies related to that cross. - Note: crossing experimental information is customized based on the need for each crop. As a result, the details of the information in your database may be different from the information shown in this manual. - - ![]({{"assets/images/edit_cross_info.png" | relative_url }}) - - ![]({{"assets/images/cross_add_progenies.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_07.md b/docs/03_managing_breeding_data/03_07.md deleted file mode 100644 index 207f249c88..0000000000 --- a/docs/03_managing_breeding_data/03_07.md +++ /dev/null @@ -1,475 +0,0 @@ ---- -title: "3.7 Managing Field Trials" -layout: doc_page ---- - - -* TOC -{:toc} - - -To view trial details on the database, click on the "Field Trials" link under the "manage" menu on the toolbar. - -![]({{"assets/images/image290.png" | relative_url }}) - -Clicking on the "Field Trials" link will bring you to the "Manage Trials" page. On this page, trials are organized according to their breeding programs. To access trial details, click on the + icon next to your breeding program. - -![]({{"assets/images/image153.png" | relative_url }}) - -Trials can be placed directly in their breeding program. Alternatively, they can be organized by using folders within each breeding program. Clicking on trial name will take you directly to the trial details page. - -![]({{"assets/images/image279.png" | relative_url }}) - -Trial Detail Page ------------------ - -Trial detail page displays important information about individual trial including breeding program, location, year, description of the trial, design, and any files associated with that trial. - -![]({{"assets/images/trial_detail_page_start.png" | relative_url }}) - -The "Navigator" section on the trial detail page allows easy access to all aspects of your trial. This section contains subsections for printing labels for your plots or plants, recording phenotypes, viewing your trial layout or design, viewing phenotypes for this trial, or conducting analyses. - -![]({{"assets/images/trial_detail_page_navigator.png" | relative_url }}) - -Adding Trials -------------- - -Only users with the account status of "submitter" may create trials. To learn how to change your account status from "user" to "submitter" visit the [*Managing Your Account*]({{site.baseurl}}{% link 01_basic_website_usage.md%}#managing-your-account) page. - -### Prerequisites - -- To add a trial, all of your accessions should already exist in the database before you begin to design a trial. If you have accessions that are not in the database, see the instructions for adding accessions . - -- Breeding program and location for your trial should also exist in the database. If you need to add breeding program and/or location to the database, see instructions for adding breeding program and location in the “Managing Breeding Programs” and “Managing locations” respectively. - -On the “Manage Trials” page, there are three alternative methods for you to add new trials: by using “Add Trial” form, “Upload Trial” form, or “Add Multi-location Trial” form. - -![]({{"assets/images/trial_create_manage_trials.png" | relative_url }}) - -### 3.7.1 Adding a trial by using “Add Trial” form - -#### Step 1. Begin the "Design new trial" workflow - -Click on “Design New Trial” to begin. - -![]({{"assets/images/trial_create_open_form.png" | relative_url }}) - -The first step in this workflow is an introduction that looks like: - -![]({{"assets/images/trial_create_form_1.png" | relative_url }}) - -Here it gives information about what is required for a trial, including that to create a new trial, you need to create a list of the accessions that you would like to use in the trial. Lists can be viewed, created, and modified with the "**lists**" tool at the upper right of the screen. For more information on lists, click [*here*]({{site.baseurl}}{% link 01_basic_website_usage.md%}#working-with-lists). - -#### Step 2. Enter "Trial Information" - -On this screen you need to enter basic information about the trial, such as breeding program and location(s). You must also select a design type, such as Complete Block Design. The design is important because it influences how your genotypes are distributed and randomized over the trial. You must first click validate before proceeding to the next step. - -![]({{"assets/images/trial_create_form_2.png" | relative_url }}) - -#### Step 3. Enter "Design Information" - -On this screen you need to specify a list of accessions to use in the experiment. This list must be a valid list of accessions. You must also specify all required design information, such as number of blocks in this case. - -![]({{"assets/images/trial_create_form_3.png" | relative_url }}) - -#### Step 4. Enter "Field Map Information" (Optional) - -On this screen you can specify how the row and column numbers will be generated for the plots in the trial. The row and column number represent a relative position of the plot in the field. If you are not exactly sure of how you will plant the plots in the field or you have an irregular (non-rectangular) layout, you can skip this step for now. This information can be added on the Trial Detail Page once the trial is saved in the database in order to reflect exactly how the plots were planted in the field. - -![]({{"assets/images/trial_create_form_4.png" | relative_url }}) - -#### Step 5. Custom Plot Naming (Optional) - -On this screen it is possible to change the format in which plot names will be generated for your trial. It is recommended to skip this step and just use the format generated by the database by default. - -![]({{"assets/images/trial_create_form_5.png" | relative_url }}) - -#### Step 6. Review Designed Trial - -On this screen you can review the trial that the database has generated. - -You will see a graphical representation of the trial. The numbers on the squares represent the plot_number of each plot and on mouse hover you can see further information about the plot. - -![]({{"assets/images/trial_create_form_6_top.png" | relative_url }}) - -You will also see a table representation of all the plots and their information. If you want to redo the randomization, you can click the "Redo Randomization" button. - -![]({{"assets/images/trial_create_form_6_middle.png" | relative_url }}) - -At the bottom there is a brief summary of the trial followed by two buttons. - -![]({{"assets/images/trial_create_form_6_bottom.png" | relative_url }}) - -#### Step 7. Add Field Management Factors to your design (Optional). - -You can add Field Management Factors by clicking "Add Field Management Factor(s) to Design". Clicking this opens a dialog to name your factor. You can name this to account for fertilizer or watering regime or inoculation or anything else. This is optional and can be added from the trial detail page afterwards. - -![]({{"assets/images/add_management_factor_name_dialog.png" | relative_url }}) - -Click "Continue" and a dialog will appear where you can specify plots for which the factor was applied. There is a select all button also. - -![]({{"assets/images/add_management_factor_dialog.png" | relative_url }}) - -#### Step 8. Saving new trial in the database. - -Once you are done reviewing the trial you can click "Confirm" to save the generated trial into the database. Once the trial has saved you will see the final completion screen: - -![]({{"assets/images/trial_create_form_7.png" | relative_url }}) - -### 3.7.2 Adding a trial from an uploaded file - -If you already have trial design layout in a spreadsheet, you can add your trial into the database by using “Upload Trial” form. To access “Upload Trial” form, click on “Upload Existing Trial(s)” button on the “Manage Trials” page. - -![]({{"assets/images/manage_trials.png" | relative_url }}) - -When you click "Upload Existing Trial(s)" you will see the following workflow. Notice that there are 5 numbered sections to the workflow. - -#### Step 1: - -The first step is to understand what the format of the trial upload is. It is important to understand that the field layout represents plots in the experiment. Each plot has a globally unique plot_name, a sequential plot_number that is unique in the trial (but not globally unique. e.g. 101, 102, 103 for three separate plots), an accession_name representing what genotype is planted in that plot, and a block_number representing design replication. Each plot can be thought of as having a row_number and a column_number representing the relative position of the plot in a grid (e.g. the top left plot is row 1 column 1 following by row 1 column 2). Each plot can be planted with an amount of seed from a seedlot, where the seedlot_name represents the specific seed packet that was used, and num_seed_per_plot and weight_gram_seed_per_plot represent amount that were transferred from the seedlot_name to the plot_name. Treatments (management factors) can be applied onto plots using additional column names in your file, where a 1 represents if the factor was applied to the plot and an empty cell means it was not applied. - -![]({{"assets/images/manage_trials_upload_trial_1.png" | relative_url }}) - -This information and more can be found by clicking "Information about file format", which shows the following: - -![]({{"assets/images/manage_trials_upload_trial_template.png" | relative_url }}) - - -##### Minimum File requirements - -- All accession names in the file must exist in the database. See adding accessions for more information. - -- The uploaded file should be XLS or XLSX file format (NOT CSV). - -- The first row (header) must contain the column names: plot_name accession_name plot_number block_number is_a_control rep_number range_number row_number col_number seedlot_name num_seed_per_plot weight_gram_seed_per_plot - -Minimal Example: - -| **plot\_name** | **accession\_name** | **plot\_number** | **block\_number** | **is\_a\_control** | **rep\_number** | **range\_number** | **row\_number** | **col\_number** | **seedlot\_name** | **num\_seed\_per\_plot** | **weight\_gram\_seed\_per\_plot** | -|----------------|---------------------|------------------|-------------------|--------------------|-----------------|-------------------|-----------------|-----------------|-------------------|--------------------------|-----------------------------------| -| 2018plot1 | my_accession1 | 101 | 1 | 1 | | | | | | | | -| 2018plot2 | my_accession2 | 201 | 2 | | | | | | | | | -| 2018plot3 | my_accession2 | 102 | 1 | | | | | | | | | -| 2018plot4 | my_accession1 | 202 | 2 | 1 | | | | | | | | - -##### File validation - -- In case of errors in the uploaded file such as missing or invalid data, a window will appear listing the specific errors in the file that must be corrected before a successful upload. - -##### Uploading a trial with Field Management Factors - -- You can upload a trial with field management factor(s) by adding additional column(s). The column header will be the factor e.g. fertilizer, watering regime, inoculation, etc. and the values in these columns will be either 1 or empty, indicating that the factor was applied to the plot or not. - -#### Step 2: - -Once you feel that your experiment field layout is in the right format, click on to the Next Step. You will see the following form which must be filled in completely: - -![]({{"assets/images/manage_trials_upload_trial_2.png" | relative_url }}) - -The trial name must be globally unique in the database. Please try to follow standard naming conventions for your group. - -First you need to validate the form, and then you can click "Upload Trial". - -#### Step 3: - -In the case where you have uploaded an experiment using accession_names that are not already present in the database, you will be taken to this screen. If the accession_names in your file are all already in the database, this step will be skipped. -The reason it is necessary for your accessions to be in the database before you can add a trial using them is that a single accession can be used among many trials and therefore must exist as a separate entity in the database; because of this it is also very important to be careful about adding wrongly duplicated accession_names into the database. From this screen it is possible to make a new list with the missing accession_names and then click "Add Accessions to the database" to immediately resolve the issue. Once all your accessions are in the database, click to move to the Next Step. - -![]({{"assets/images/manage_trials_upload_trial_3.png" | relative_url }}) - -#### Step 4: - -In the case where you have uploaded an experiment using seedlot_names that are not already present in the database, you will be taken to this screen. If the seedlots in your file are all already in teh database, this step will be skipped. -The reason it is necessary for your seedlots to be in the database before you can add a trial using them is that a ginel seedlot can be used among many trials and therefore must exist as a separate entity in the database. From this screen it is possible to add the missing seedlots; you can either upload an XLS or XLSX file to add many at once or you can add them one by one. Once all your seedlots are in the database, click to move to the Next Step. - -![]({{"assets/images/manage_trials_upload_trial_4.png" | relative_url }}) - -#### Step 5: - -If there are any other errors with your file, such as if the plot_names are not globally unique in the database or your plot_numbers are not unique in your trial or row_number is not an integer or any other error, you will see the errors listed in the red box. It is up to you to correct these errors in your file. Simply open up the file you selected earlier in Excel and correct the issues and then save the file. Then you can click "Submit Trial" and it will resubmit it for you. You can continue to edit your file here and submit as many times as you need until it is accepted. - -![]({{"assets/images/manage_trials_upload_trial_5.png" | relative_url }}) - -#### Completion screen - -Whether you were lucky enough to submit your trial successfully on Step 2 or if you tried many times on Step 5, once your trial has been saved in the database you will see the following screen: - -![]({{"assets/images/manage_trials_upload_trial_6.png" | relative_url }}) - - -### 3.7.3 Multi-location trials - -To add multi-location trials, simply select the multiple locations while using the 'Add Trial' form. - -This will create a separate trial for each selected location, but they will share the same design and will be grouped in a single folder. - -By default each trial design will have a fresh randomization, but if desired you may check the "Use same randomization for all locations" option. - -### 3.7.4 Viewing Plot Layout and Trait HeatMap - -#### Viewing plot layout - -In the "Field Layout Tools and Phenotype Heatmap" section of a Trial Detail page, the trial physical layout is displayed by default. The relative position of the plots will be displayed based on the row and column positions given to the plots during the trial creation or upload steps. The plots are color-coded based on the plot's rep and block numbers and whether or not it is used as a check. Hover the mouse over the plot to see details about a specific plot. - -![]({{"assets/images/fieldmap_trial_layout.png" | relative_url }}) - -#### Viewing plot layout for multiple trials - -If there is more than one trial grown in the same physical field, the trial layouts of all of the trials can be shown together if the trials share these properties: - -
    -
  • Each trial has the same year
  • -
  • Each trial has the same location
  • -
  • The location type of the trials' location is set to Field
  • -
  • The row and column positions of all of the plots (across the related trials) don't overlap. For example, trial #1 starts at row 1 and trial #2 starts at row 10.
  • -
- -When these conditions are met and you check the "Select Trials in Same Field" checkbox, the plots from all of the related trials will be displayed on the same field layout. The plots will be color-coded by trial. The planting order and harvest order downloads will include the plots from all of the displayed trials in the order in which the plots occur in the field. - -![]({{"assets/images/fieldmap_multi_trial_layout.png" | relative_url }}) - -#### Tracking plot images on fieldMap - -Plot images can be seen on fieldMap if a plot is associated to any image. - -![]({{"assets/images/fieldmap_plot_image.png" | relative_url }}) - -To view plot image(s), click on a plot, a dialog will appear. - -![]({{"assets/images/fieldmap_view_plot_image.png" | relative_url }}) - -On the appeared dialog, click on View plot images. To see more images if a plot has more that 2 images, click on See more images... Medium size of an image can be viewed by clicking on an image. - -![]({{"assets/images/fieldmap_display_plot_image.png" | relative_url }}) - - -#### Viewing assayed trait heatmap - -Phenotype heatmap can be viewed by selecting a specific assayed trait from the selectbox drop-down. Mousing over the plots, highlights the plot in green and also displays the plot's field information including the selected trait's phenotype value. - -![]({{"assets/images/heatmap_assayed_trait_view.png" | relative_url }}) - -[//]: # ![]({{"assets/images/trial_detail_page_trait_heatmap.png" | relative_url }}) - -#### Suppressing Plot Phenotype - -Clicking on a plot on the heatmap would display a dialog that has a button for suppressing a plot phenotype value for a given trait. A suppressed plot value can be excluded during trial analysis and phenotype download. - -![]({{"assets/images/trial_detail_page_suppress_phenotype.png" | relative_url }}) - -### 3.7.5 Adding additional information in the “Trial Detail” page - -After you added a new trial to the database, you can edit trial details or add more information for that trial through the“Trial Detail” page. - -#### Uploading Physical Trial Layout - -You can upload physical trial layout by clicking on the “Upload trial coordinates” button on the “Trial Detail” page. - -![]({{"assets/images/image332.png" | relative_url }}) - -Please check file format carefully. You can find file format information by clicking on the “Spreadsheet format” on the “Upload trial coordinates” window. - -![]({{"assets/images/image340.png" | relative_url }}) - -Spreadsheet format: - -![]({{"assets/images/image190.png" | relative_url }}) - -#### Physical Trial Layout File requirements - -- All plot names in the file must exist in the database. - -- The uploaded file should be tab delimited (txt). - -- The first row (header) must contain the column names - -Example: - -| plot\_name | row\_number | col\_number | -|------------|-------------|-------------| -| plot1 | 1 | 1 | -| plot2 | 1 | 2 | -| plot3 | 1 | 3 | - -Select the trial layout coordinates file that you want to upload for this trial, then click “OK” button to upload the file. - -![]({{"assets/images/image79.png" | relative_url }}) - -The following message is displayed after the coordinates are uploaded. - -> - -The field layout can be viewed by clicking on the “Trial Heatmap Section” to see a drop-down of the field map. - - - -#### Downloading Field Map Spreadsheet - -Field map spreadsheet can be downloaded if the trial has field coordinate (row and column numbers) uploaded for it plots. -To download, click on the Download FieldMap Layout link on the Trial Heatmap section. - -![]({{"assets/images/field_map_download_link.png" | relative_url }}) - -A dialog will appear, click on the submit button to download. - -![]({{"assets/images/field_map_download_dialog.png" | relative_url }}) - -Click to view downloaded spreadsheet. - -![]({{"assets/images/field_map_download_spreadsheet.png" | relative_url }}) - -#### Editing Physical Trial Layout - -“Usage Help” link contains information on how to edit physical trial layout. - -![]({{"assets/images/image332.png" | relative_url }}) - -![]({{"assets/images/image319.png" | relative_url }}) - -![]({{"assets/images/image317.png" | relative_url }}) - -There are three different options for editing trial layout: - -- Replacing plot accession by clicking on the plot in the layout. - -- Replacing trial accession by using “Edit Field Map” link. - -- Substituting plot accessions by using “Edit Field Map” link. - -When you move a cursor over a plot on the trial layout, information for that plot appears. - -Picture2modified.png - -To edit a specific plot, clicking on that plot. Entering new accession on the “Replace Plot Accession” form, then clicking on “Replace Plot Accession” button. - -Picture3modified.png - -To replace an accession (in every plot/plant of that accession), clicking on “Edit Field Map” button. - -![]({{"assets/images/image173.png" | relative_url }}) - -On the “Edit Field Map” window, clicking on “Replace Accession” button. - -![]({{"assets/images/image87.png" | relative_url }}) - -Selecting any accession that you want to replace and entering your new accession, then clicking “Replace Trial Accession” button. - -![]({{"assets/images/image85.png" | relative_url }}) - -You can switch plot accessions between any two plots by clicking on “Substitute Accession” button. - -![]({{"assets/images/image162.png" | relative_url }}) - -On the “Substitute Plot Accession” form, selecting the two plots that you want to switch, then clicking on the “Substitute Plot Accession” button. - -![]({{"assets/images/image282.png" | relative_url }}) - -### 3.7.6 Downloading the Trial Layout from the “Trial Detail” page - -Click on "Download Layout" on the Trial Detail page. - -![]({{"assets/images/image348.png" | relative_url }}) - -The trial layout includes all information regarding the observation units in the experiment. The observation units can be plots, plants, or subplots. The trial layout can include trial design information such as the block_number and rep_number. It can also include physical map information such as the row_number and col_number, if that information is available for the trial. -The trial layout also includes information regarding treatments that have been applied in the field. -Optionally, the layout can give information regarding accession's global performance for a list of traits. - -![]({{"assets/images/image347.png" | relative_url }}) - -### 3.7.7 Adding Plant Entries To Your Trial - -After you added a new trial to the database you can choose to add plant entries to your trial. Adding plant entries enables plant level phenotyping. It is generally better to enter data at the plant level into the database because it is always possible to calculate plot level phenotypes from the individual plant data. - -Plant entries can be added to your trial in two ways: -1) Automatically generated by the database. The only input required is the number of plants per plot. -2) Uploaded in an XLS or XLSX file. This allows you to specifically name your plant entries. - -These two options are available in the "Plant Entries" section on the Trial Detail Page, as shown in the screen shot below. - -![]({{"assets/images/trial_detail_page_add_plant_entries.png" | relative_url }}) - -#### Automatically Generate Plant Entries - -Clicking on "Add plant entries" opens the following dialog box. The only input required is the number of plants per plot. This will create plant entries that are named as a concatenation of the plot_name and the plant's index number e.g. plot_name_plant_1 - -![]({{"assets/images/trial_detail_page_add_plant_entries_autogenerated.png" | relative_url }}) - -#### Upload Plant Entries - -Alternatively, you can choose to upload an XLS or XLSX file that contains the names of the plant entries. Clicking on "Upload plant entries" opens the following dialog box. - -![]({{"assets/images/trial_detail_page_add_plant_entries_upload.png" | relative_url }}) - -Clicking on "Spreadsheet format" will give you information about the XLS or XLSX file to upload. Clicking this will open the following dialog box. - -![]({{"assets/images/trial_detail_page_add_plant_entries_upload_info.png" | relative_url }}) - -This shows you that the files requires the header to contain "plot_name" and "plant_name". The plot_name must exist in the database already and the plant_name must be unique in the database. - -Along with the file, you must specify "number of plants per plot". This is intended to be the total number of plants that were plants. If the file you upload shows three plants in one plot and four plants in another plot, that is fine. - -### 3.7.8 Adding Tissue Sample Entries To Your Trial - -Some trials require tissue samples to be collected from plants in a field trial. The database will generate these tissue sample identifiers for you and will maintain all relationships with the plant, plot, accession, etc. To begin, go to the Design section of a trial's detail page and open the "tissue sample entries" section. -Please note that tissue samples are directly related to plants, therefore your trial requires plants before you can add tissue samples. - -![]({{"assets/images/manage_trials_tissue_sample_default.png" | relative_url }}) - -When you click on "Add tissue sample entries" you will see a dialog where you specify the number of tissue samples you require per plant. Once you have specified how many tissues samples, you can give specific words to distinguish samples, such as "root" or "stem", as seen below. - -![]({{"assets/images/manage_trials_tissue_sample_create.png" | relative_url }}) - -Once you have added tissue sample entries they will appear in the design section of the trial as seen below. - -![]({{"assets/images/manage_trials_tissue_samples.png" | relative_url }}) - -Each tissue sample has a detail page where you can add information about the sample, such as if it is in transit or in storage somewhere. - -![]({{"assets/images/manage_trials_tissue_sample_detail.png" | relative_url }}) - -The related stocks section near the bottom of this detail page displays the relationships between all stocks, including tissue samples. - -![]({{"assets/images/manage_trials_tissue_sample_related_stock.png" | relative_url }}) - -### 3.7.9 Uploading GPS Coordinates For Plots - -You can upload GPS coordinates for the plots in your trial. There is a link on the Trial Detail Page as shown below. - -![]({{"assets/images/trial_detail_page_add_plot_gps.png" | relative_url }}) - -Clicking on this link will bring up the following dialog. - -![]({{"assets/images/trial_detail_page_add_plot_gps_dialog.png" | relative_url }}) - -Here you can upload an XLS or XLSX file. To see information on the format of the file that should be uploaded, click on "Spreadsheet format". This will bring up the following dialog. - -![]({{"assets/images/trial_detail_page_add_plot_gps_upload_info.png" | relative_url }}) - -This dialog tells you that the file must be XLS or XLSX and must contain: plot_name WGS84_bottom_left_x WGS84_bottom_left_y WGS84_bottom_right_x WGS84_bottom_right_y WGS84_top_right_x WGS84_top_right_y WGS84_top_left_x WGS84_top_left_y -The GPS coordinates should be WGS84 format and specify a four-pointed polygon around the plot. - -### 3.7.10 Uploading Additional Files To Trial - -It may be of interest to you to upload additional documents, images, or recordings to your trial. To do this, scroll down to the "Uploaded Additional File" section on the trial detail page. From here you can view and download any of these additional files. - -![]({{"assets/images/trial_upload_additional_file.png" | relative_url }}) - -To upload an additional file, click on the "Upload Additional Files" link. A dialog will appear where you simply select your desired file. For information, you can click "Upload information" to see the following message. - -![]({{"assets/images/trial_upload_additional_file_info.png" | relative_url }}) - -### 3.7.11 Deleting Trial Data - -To delete a trial data, click on the "Delete trial data" section. There are links to delete traits, layout and trial entry data. - -![]({{"assets/images/trial_detail_page_delete_trait1.png" | relative_url }}) - -To delete assayed trait data, click on "Delete trait data" link. On the appeared dialog, confirm deletion by clicking on the "Select Traits For Deletion" button, then select one or more traits to delete from the trial. - -![]({{"assets/images/trial_detail_page_delete_trait2.png" | relative_url }}) - -![]({{"assets/images/trial_detail_page_delete_trait3.png" | relative_url }}) - -To delete trial layout data, click on the "Delete layout data" link. Confirm deletion on the appeared dialog. - -To Delete trial entry, click on "Delete trial entry" link. Confirm deletion on the appeared dialog. diff --git a/docs/03_managing_breeding_data/03_08.md b/docs/03_managing_breeding_data/03_08.md deleted file mode 100644 index 3f08cf989c..0000000000 --- a/docs/03_managing_breeding_data/03_08.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: "3.8 Managing Genotyping Plates" -layout: doc_page ---- - - -* TOC -{:toc} - - -Genotyping Plates represent the content of a genotyping plate sent to a genotyping facility (e.g. samples in specific wells). To streamline this process, it is possible to upload this information or let the database create a plate for you. -Once the genotyping plate is saved in the database it is then possible to export the information directly to genotyping facilities that are BrAPI compliant. The genotyping facility can then provide status information to us via BrAPI. - -To begin go to Manage->Genotyping Plates. - -![]({{"assets/images/manage_genotyping_trials.png" | relative_url }}) - -Here the genotyping plates are divided by Breeding Program. These sections can be expanded by clicking on one. - -![]({{"assets/images/manage_genotyping_trials_expand.png" | relative_url }}) - -### 3.8.1 Adding a New Genotyping Plate - -To begin, click on "Add Genotyping Plate". Notice that this form is split into three sections: "Plate Information", "Well Information", and "Confirm". The first section is for defining information about the genotyping plate, such as a Plate identifier, plate format (96 well), etc. The second section is for defining the samples in the wells, such as sample names, sample concentrations, well position, etc. The final section is for Submitting the info. - -All fields in the Plate Information section are required. - -![]({{"assets/images/manage_genotyping_trials_add_trial.png" | relative_url }}) - -In the Well Information section you can choose to either 1) Upload an XLS or XLSX spreadsheet with your sample layout or 2) let the database create the sample layout. - -![]({{"assets/images/manage_genotyping_trials_add_trial_wells.png" | relative_url }}) - -If you choose to upload an XLS or XLSX spreadsheet, the Spreadsheet Template info requires the following: - -![]({{"assets/images/manage_genotyping_trials_upload_template.png" | relative_url }}) - -In either case, the sample identifier is generally a concatenation of Plate name and well position, e.g. MyGenotypingTrial1_A01. -In either case, you need to provide a "source_observation_unit_name" for each sample. This can be a tissue sample name, a plant name, a plot name, or an accession name; however, in any case, the identifier must already exist in the database. This allows us to link the sample in the well to specific field trial plots, or, plants, or tissue_samples. If you only know which accession is in the well, you can use the accession name. - -In the final Confirm section you can decide whether to submit this information to the genotyping facility you selected. This requires that the genotyping facility is BrAPI compliant to work. - -![]({{"assets/images/manage_genotyping_trials_add_trial_confirm.png" | relative_url }}) - -### 3.8.2 Genotyping Plate Detail Page - -If you open a specific genotyping plate, it will take you to the detail page. -Here you can see the Accessions used in the plate (if you created the trial and the source_observation_unit_names you used were plots, this will still work because we know the accession of the plot or plant or tissue sample). - -![]({{"assets/images/manage_genotyping_trials_detail_info.png" | relative_url }}) - -Further down you can see a graphical representation of your plate with well positions. This can be 96 well or 384 well depending on your plate format. - -![]({{"assets/images/manage_genotyping_trials_detail_table.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_09.md b/docs/03_managing_breeding_data/03_09.md deleted file mode 100644 index 1865e06567..0000000000 --- a/docs/03_managing_breeding_data/03_09.md +++ /dev/null @@ -1,249 +0,0 @@ ---- -title: "3.9 Using Field Book App" -layout: doc_page ---- - - -* TOC -{:toc} - - -![]({{"assets/images/image21.png" | relative_url }}) - -SGN databases support the Android Field Book App for collecting phenotypic data in the field with tablet computers. The app is available here: - -[*https://play.google.com/store/apps/details?id=com.fieldbook.tracker*](https://play.google.com/store/apps/details?id=com.fieldbook.tracker) - -- The app can also be downloaded directly from the Google Play store. There is no charge for the app. - -- Field Book App requires two files for collecting data: Field layout file and trait file. - -- SGN databases can generate the field layout file and trait file, which can be downloaded onto your computer, then transferred to an Android tablet device. - -A typical workflow ------------------- - -1. Creating a [*field layout file*](#creating-layout-files) based on the design of field trial - -2. Creating a [*trait file*](#creating-trait-files) from the list of traits - -3. Downloading the field layout file and trait file from the database to your computer - -4. Downloading the field layout file and trait file to the tablet (where the Field Book App is installed) - -5. Collecting phenotypes - -6. Exporting phenotypes from Field Book App to your computer - -7. [*Uploading the exported phenotype file*](#uploading-pheno-files) from your computer to the database - -3.9.1 Creating Field Layout Files for the Field Book App {#creating-layout-files} --------------------------------------------------------- - -There are two alternative methods for creating “Field Layout Files”. - -1. Using “Field Book Tools” page - -2. Using “Trial Detail” page. - -### 3.9.1.1 Creating “Field Layout Files” by using “Field Book Tools” page. - -To access “Field Book Tools” page, clicking on “Field Book App” in the “Manage” menu. - -![]({{"assets/images/image170.png" | relative_url }}) - -On the “Field Book Tools” page, clicking on “New” - -![]({{"assets/images/image253.png" | relative_url }}) - -On the “Download Fieldbook” window, selecting trial name and data level (plots or plants), then clicking on “Submit” button. -A treatment can be selected, which allows you to record phenotypes based on treatment application. -A list of traits can be selected, which provides a summary of an accession's global performance for those traits in the Fieldbook. - -![]({{"assets/images/image337.png" | relative_url }}) - -If the field book layout file was successfully created, a pop-up window will indicate that the field book layout file was saved successfully. Clicking on the file name will immediately download the file onto your computer. The file is also available to download on the “Field Book Tools” page, if you need to re-download it. - -![]({{"assets/images/image183.png" | relative_url }}) - -To download field layout file to your computer, clicking on “Download File”, the file can then be transferred to your tablet. If you no longer want to keep the field layout file, clicking on “Delete Layout File”. - -![]({{"assets/images/image257.png" | relative_url }}) - -### 3.9.1.2 Creating “Field Layout Files” by using “Trial Detail” page. - -To create “Field Layout Files”, go to the “Trial Detail” page of the trial that you want to create the file. On the “Trial Detail” page, scrolling down to the bottom of the page to find “Android Field Book Layout” in the “Files” section, then clicking on the “Create Field Book” link. - -![]({{"assets/images/image89.png" | relative_url }}) - -Clicking on the “Create Field Book” link will open a new window showing the name of the trial that you selected, as well as data level (plots or plants). -A treatment can be selected, which allows you to record phenotypes based on treatment application. -A list of traits can be selected, which provides a summary of an accession's global performance for those traits in the Fieldbook. -To proceed, clicking on “Submit” button. - -![]({{"assets/images/image161.png" | relative_url }}) - -If the field book layout file was successfully created, a pop-up window will indicate that the field book layout file was saved successfully. Clicking on the file name will immediately download the file onto your computer. The file is also available to download on the “Field Book Tools” page, if you need to re-download it. - -![]({{"assets/images/image183.png" | relative_url }}) - -To download field layout file to your computer, clicking on “Download File”, the file can then be transferred to your tablet. If you no longer want to keep the field layout file, clicking on “Delete Layout File”. - -![]({{"assets/images/image94.png" | relative_url }}) - -3.9.2 Creating Trait Files for the Field Book App {#creating-trait-files} -------------------------------------------------- - -Steps to Create a Trait File: - -### 3.9.2.1 Creating a Trait List - -After you logged in, lists can be created and managed using the Search Wizard or the "Lists" link. For more information on how to create lists, click [*here*]({{site.baseurl}}{% link 01_basic_website_usage.md%}#working-with-lists). - -![]({{"assets/images/image325.png" | relative_url }}) - -### 3.9.2.2 Creating a Trait File - -After you have your trait list, clicking on the "**Field Book App**" link found under the "**Manage**" menu tab. This will take you to the “Field Book Tools” page. - -![]({{"assets/images/image133.png" | relative_url }}) - -To create a new trait file, finding the heading “Trait Files”, then clicking on the “New” link. - -![]({{"assets/images/image146.png" | relative_url }}) - -Clicking on the "New" link will open a dialogue box titled "Create Trait File". Please enter your “Trait file name” and select “List of traits to include” from drop-down list that you previously created. You can only use traits included in the list. Check the box titled "Include Notes Trait" if you would also like to record and upload general plot notes in the field. Click "OK" to submit. - -![]({{"assets/images/image134.png" | relative_url }}) - -If your trait file was successfully created, a new window will indicate that the trait file was saved, then clicking on “Close”. - -![]({{"assets/images/image174.png" | relative_url }}) - -After the trait file was saved, you will see your file listed in the “Field Book Tools” page. Clicking on “Download” link to download the trait file to your computer. - -![]({{"assets/images/image163.png" | relative_url }}) - -After downloading the trait file to your computer, the file can be transferred to an Android Tablet. You need the Android Field Book App to open the file. The Android Field Book App can be downloaded at: *http://www.wheatgenetics.org/bioinformatics/22-android-field-book* - -3.9.3 Transferring Files from Your Computer to Android Tablet -------------------------------------------------------------- - -### Files on your computer - -After downloading, Field Layout files and Trait files can be found in the “Downloads” folder of your computer. Field Layout files on your computer will have a prefix “fieldbook\_layout\_” added to the beginning of the file name. For example: **"2014-01-28\_19:14:34\_Trial Demo\_location 6767.xls"** on the the database website will be saved as **"field\_book\_layout\_2014-01-28\_19:14:34\_Trial Demo\_location 6767.xls"** on your computer. - -![]({{"assets/images/image142.png" | relative_url }}) - -The files can be transferred to Android tablet by copying the files into the tablet's Internal Storage File. - -### Files on your Android tablet - -To transfer Field Layout file and Trait file to your Android tablet, connecting an Android tablet to your computer, then clicking on tablet icon on your computer. Clicking on the tablet icon will open a window showing an “Internal Storage” file. - -![]({{"assets/images/image16.png" | relative_url }}) - -After you installed the Android Field Book App, all files for the app are stored in the “fieldBook” folder within the “Internal storage” folder. - -![]({{"assets/images/image314.png" | relative_url }}) - -Within the “fieldBook” folder, there are five sub-folders: - -- field\_export - -- field\_import - -- plot\_data - -- resources - -- trait - -**Field Layout files must be copied into the “field\_import” folder.** - -![]({{"assets/images/image285.png" | relative_url }}) - -**Trait files must be copied into the “trait” folder.** - -![]({{"assets/images/image252.png" | relative_url }}) - -You can either drag and drop, or copy the Field Layout file and the Trait file from your computer to the folders in your Android tablet. - - -3.9.4 Setting up “Field Book App” for data collection ------------------------------------------------------ - -After you transferred the Field Layout file and Trait file from your computer to Android tablet, you still need to set up “Field Book App” on your tablet for data collection. - -To set up the Field Book App: - -1. **To open the Field Book App in the Android Tablet, clicking on the Field Book App icon, which is a green rectangle.** -Field Book App.png - -2. **To import Field Layout files, clicking on the "Fields" section of the main menu of the Field Book App.** -Import Field Layout File .png - -Clicking on the "Fields" tab will open a new dialogue that will let you select the file that you want to import. - -Choose Field File.png - -Choosing a Field File will generate a new dialogue that will ask you to choose between an Excel or CSV format. Since the data from the database is in Excel format, choose the Excel option. - -Fields Excel.png - -After submitting the file format, a final dialogue box will appear. Please provide information about the file that you want to import. Please ensure that "plot\_name" is set as the unique identifier. To finalize the process, clicking “OK” button. - -Field Import Info.png - -3. **To import Trait Files, clicking on the "Traits" tab on the main menu of the Field Book App.** -Import Field Layout Files Tablet.png -Then, clicking on the three dots symbol found on the upper right corner of the Field Book screen. This will open a drop down menu with the choices "Import" and "Export". Clicking on "Import" -import traits.png -Clicking on "import" will open a new dialogue that displays a list of trait files that you can select to import to the Field Book App. -import traits dialogue.png -The trait file is now imported into the Field Book App. The traits page will show all trait files and available traits. -traits page.png - - -3.9.5 Exporting Files from Field Book App ------------------------------------------ - -Data that were collected on the Field Book App can be exported back to your tablet folder, which can then be transferred to your computer. - -To export files containing data from the Field Book App to your tablet, clicking on the "Export" link on the main menu page of the Field Book App. - -Export Data.png - -Clicking on the "Export" link will open a new dialogue window. To ensure that data are exported in a correct format for the database, checking the "Database Format" box, then clicking on “OK” button. - -Export Data Database Format.png - -The exported file can then be found in the “field\_export” sub-folder within the “fieldBook” folder on your tablet. Once you connect your tablet to your computer, you can directly transfer the file to your computer. - -![]({{"assets/images/image254.png" | relative_url }}) - -![]({{"assets/images/image158.png" | relative_url }}) - - -3.9.6 Uploading Phenotype Files to an SGN database {#uploading-pheno-files} ----------------------------------------------- - -To upload phenotype files to the database, clicking on “Field Book App” in the “Manage” menu. - -![]({{"assets/images/image302.png" | relative_url }}) - -On the “Field Book Tools” page, clicking on “Upload” link in the “Uploaded Phenotype Files” section. - -![]({{"assets/images/image165.png" | relative_url }}) - -Clicking on the "Upload" link will open a new dialogue asking you to choose a file that you want to upload to the database website. Please ensure that "plot\_name" is the first column of the file to be uploaded. To make sure that the file has the correct format for uploading, click on the “Verify” button. After the file format has been verified, click on the “Store” button. - -![]({{"assets/images/image131.png" | relative_url }}) - -The list of uploaded phenotype files can be found on the Field Book Tools page - -![]({{"assets/images/image265.png" | relative_url }}) - -The uploaded files will also be seen in the corresponding “Trial Detail” page. - -Traits Assayed.png diff --git a/docs/03_managing_breeding_data/03_10.md b/docs/03_managing_breeding_data/03_10.md deleted file mode 100644 index 829fbedd65..0000000000 --- a/docs/03_managing_breeding_data/03_10.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: "3.10 Managing Phenotypic Data " -layout: doc_page ---- - - -* TOC -{:toc} - - -To facilitate uploading process for phenotypic data, “Manage Phenotypic Data” page provides two options for uploading: Field Book Phenotype file in database format and phenotype file in Excel (.xls or .xlsx) file format. To access “Manage Phenotypic Data” page, clicking on “Phenotyping” in the “Manage” menu. - -![]({{"assets/images/image178.png" | relative_url }}) - -3.10.1 Uploading Fieldbook Phenotypes ---- - -### Export Field Book Database File - -The database upload of Field Book phenotype data relies on the "Database" format from the Field Book. Please make sure to export the "Database" format from the Field Book if you intend to upload the data using the Field Book Upload we describe below. If you prefer to use the "Table" format that the Field Book exports, you can modify this format to work with the Speadsheet Upload we describe below. - -### Upload Field Book Database File - -To upload a Field Book Phenotype file in a database format, click the “Upload Fieldbook” link - -![]({{"assets/images/image185.png" | relative_url }}) - -The “Upload Fieldbook” link on this page and “Upload” link on the “Field Book Tools” page open the same dialogue. Please follow instructions for uploading phenotypic files on [*"Using Field Book App"*]({{site.baseurl}}{% link 03_managing_breeding_data/03_06.md %}) page. - -![]({{"assets/images/image112.png" | relative_url }}) - -3.10.2 Uploading Spreadsheet Phenotypes ---- - -To upload a phenotype file in an Excel (.xls or .xlsx) file format, click the “Upload Spreadsheet” link. - -![]({{"assets/images/image288.png" | relative_url }}) - -Please specify “Data Level” (Plots or Plants) and select the Excel file that you want to upload. - -![]({{"assets/images/image68.png" | relative_url }}) - -### Generating Spreadsheet File - -You can find more file format information by clicking on “Spreadsheet Format” link. Clicking on "Spreadsheet Format" will open the following dialog. - -![]({{"assets/images/phenotype_spreadsheet_upload_help_dialog.png" | relative_url }}) - -Clicking on "Create Phenotyping Spreadsheet" will bring up a dialog where you can indicate the trial(s) you are interested in and the trait list you are interested in. Clicking "Submit" will download the xlsx file onto your computer, where you can then fill in the phenotypes. - -![]({{"assets/images/create_phenotype_spreadsheet_dialog.png" | relative_url }}) - -### Uploading Spreadsheet File - -To ensure that the file has a correct format for uploading, click on the “Verify” button. This will check the contents of the file and also perform quality checks on the values in the file. These checks include checking the trait definition for categorical values, minimum and maximum values, and data type checking. It will also check if there are already values uploaded for the given observation units and traits. If there are, there is an option to overwrite the existing values with the new values in your file. If the file is valid, only then can you click "Store" to store the information in the database. - -![]({{"assets/images/phenotype_spreadsheet_upload_dialog.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_11.md b/docs/03_managing_breeding_data/03_11.md deleted file mode 100644 index e6dbf040f7..0000000000 --- a/docs/03_managing_breeding_data/03_11.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: "3.11 Managing Barcodes" -layout: doc_page ---- - -SGN databases provide tools for generating barcodes for stock identification. To access “Barcode Tools” page, clicking on “Barcodes” in the “Manage” menu. - -![]({{"assets/images/image82.png" | relative_url }}) - -“Barcode Tools” page provides four options for generating barcodes: - -- Single barcode - -- Multiple barcodes - -- Plot phenotyping barcodes - -- Trial barcodes - -To generate single barcode, clicking on “Generate Barcode” link on the “Barcode Tools” page. - -![]({{"assets/images/image274.png" | relative_url }}) - -In the “Generate Barcode” section, specify the name of the barcode, size of the barcode, then clicking on “Generate Barcode” - -![]({{"assets/images/image245.png" | relative_url }}) - -The database will generate a barcode for your stock. The barcode can be printed for your stock identification. It also appears on its corresponding stock page. - -![]({{"assets/images/image264.png" | relative_url }}) - -If you have a list of stocks that you want to generate barcodes, you can use “Download Stock Barcodes” section. You have three options for entering stock names: - -1. Typing in stock names, or copy and paste from other file into the box (1) - -2. Choosing a list of stocks from your “Lists” (2), and transferring the list into the box (1) by clicking on “paste” button. - -3. Uploading a “Tab-delimited Text File” with stock names. - -4. Select an optional printing format from the available formats. - -You can select printer settings that you prefer in the “Printer Settings” section. After you enter stock names and specify printer settings, clicking on “Download Barcodes” button at the bottom of the page. - -![]({{"assets/images/image248.png" | relative_url }}) - - -If you have a list of plots that you want to generate phenotyping barcodes, you can use “Download Plot Phenotyping Barcodes” section. You have three options for entering plot names: - -1. Typing in plot names, or copy and paste from other file into the box (1) - -2. Choosing a list of plots from your “Lists” (2), and transferring the list into the box (1) by clicking on “paste” button. - -3. Uploading a “Tab-delimited Text File” with plot names. - -![]({{"assets/images/plot_pheno_barcode.png" | relative_url }}) - -If you have a list of trials that you want to generate barcodes, you can use “Download Trial Barcodes” section. You have three options for entering trial names: - -1. Typing in trial names, or copy and paste from other file into the box (1) - -2. Choosing a list of trial from your “Lists” (2), and transferring the list into the box (1) by clicking on “paste” button. - -3. Uploading a “Tab-delimited Text File” with trial names. - -![]({{"assets/images/trial_barcode.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_12.md b/docs/03_managing_breeding_data/03_12.md deleted file mode 100644 index c532fab8f9..0000000000 --- a/docs/03_managing_breeding_data/03_12.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: "3.12 Using the Label Designer" -layout: doc_page ---- - - -* TOC -{:toc} - - -Breedbase provides an interactive design tool for creating custom labels. To access the Label Desginer, click on “Label Designer” in the “Manage” menu. The following sections explain your many options as you advance through each step of the design workflow. - -### 3.12.1 Select a Datasource - -The first step is to select a data source. Since the label designer can generate labels for different data types, you can optionally filter the source selection by the data type you're interested in. Then, select a field, genotyping, or crossing trial to populate your labels with the trial design information. Or select a list to populate your label with the list contents. -For data sources with multiple levels of information you will also be asked to pick a level (plot, plant, etc.) before proceeding. To generate plot-level labels for more than one trial at once, select a list of trials as the source and plot as the level. - -![]({{"assets/gifs/gif1.gif" | relative_url }}) - -### 3.12.2 Set Page and Label Size - -Now choose whether to create a new design or load a saved design. If you choose new, you will be prompted to select a page size and label size. -If you do not see your page or label size as an option, then select Custom and enter your desired dimensions in pixels, or 1/72nds of an inch. -If you choose saved, you will be prompted to select a saved design then will be taken directly to the design step with the saved design elements preloaded. - -![]({{"assets/gifs/gif2.gif" | relative_url }}) - -### 3.12.3 Design Your Label - -Below is a draw area where you can begin adding elements to your label. First select a type, then field, size, and font, then click 'Add' -You can add text to an exisiting field or create a completely custom field by clicking 'Create Custom Field' -Once added, you can drag and drop elements, or delete them by clicking on the red box in their upper left corners. -Barcodes can also be resized by dragging on the green box in their lower right corners. -If you are creating labels for a trial it is highly recommended to include a barcode encoding your plot, plant, or tissue sample names. -These are your unique identifiers that will need to included with any phenotypic or genotypic measurements loaded into the database. -When you are satisfied with your design, click next! - -![]({{"assets/gifs/gif3.gif" | relative_url }}) - -### 3.12.4 Set Page and Label Size and Sorting Options - -Last step! Here you can tweak your formatting and page layout, save your design, or download your labels. -The additional settings dialog will allow you to fine tune the print margins and margins between labels. The units are pixels or 1/72nds of an inch. It's not recommended to change these until you've already done a test print. Here you can also set sorting options to adjust the order of the individual labels. You can sort by 1-3 different properties (the available properties will depend on the data type of the labels). For example, if you're printing plot-level labels for multiple trials, you can sort first by trial name and then by plot number. -You can also set the # of copies per label, filter by rep, or download just the first page for test purposes. -To save you're design just type a unique name and hit save. This will save your design to your list manager where you can set it to public to share it with others. -Finally if you are ready just hit download to generate and download your labels! - -![]({{"assets/gifs/gif4.gif" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_13.md b/docs/03_managing_breeding_data/03_13.md deleted file mode 100644 index d72beb6159..0000000000 --- a/docs/03_managing_breeding_data/03_13.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: "3.13 Managing Downloads" -layout: doc_page ---- - -You can download phenotype, trial meta-data, pedigree, GBS genotype and GBS genotype QC files from the database to your computer by using “Lists”. To download, clicking on “Download” in the “Manage” menu. - -![]({{"assets/images/image255.png" | relative_url }}) - -For each category, you can select a list of accessions from your “Lists” to download their phenotypes, pedigree, GBS genotype, GBS genotype QC. In the case of downloading trial meta-data, you would provide a list of trials, while for downloading phenotype and GBS genotype QC, you can also use a list of trials or traits. - -![]({{"assets/images/trial_meta_data_download.png" | relative_url }}) - -![]({{"assets/images/image287.png" | relative_url }}) - -![]({{"assets/images/image182.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_14.md b/docs/03_managing_breeding_data/03_14.md deleted file mode 100644 index 24be778832..0000000000 --- a/docs/03_managing_breeding_data/03_14.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: "3.14 Managing ODK Data Collection" -layout: doc_page ---- - - -* TOC -{:toc} - - -To access this page go to Manage and then ODK Data Collection. -ODK is used for remotely collecting data on Android and IOS devices. We currently are working to support two ODK service providers, namely ONA and SMAP. We are using ONA to collect crossing information, including all lab activities following seed production. We are using SMAP for phenotypic data collection. - -### 3.14.1 ONA Crossing Information - -#### Managing ONA Crossing Information - -![]({{"assets/images/odk_ona_management.png" | relative_url }}) - -To begin collecting data using the ONA ODK form you must first have a crossing plan in the form of a Cross Wishlist. To do this from this page, click the "Export Cross Wishlist to ONA" button. Please refer to the "Create Cross Wihlist" help section for more information. -It is possible to view the current available cross wishlists by clicking the "Export Cross Wishlist to ONA" button and then clicking "Available Cross Wishlists". - -Once your cross wishlist is available, you can use your mobile ODK application to record crosses being done realtime. You can also record all laboratory activities following seed extraction up to greenhouse plantlet hardening. - -As you collect data using your mobile ODK application, your responses will be synchronized with our database. The "Schedule Import for Selected Form" section gives you options to perform the import daily or more frequently. It is also possible to initiate a data import from ONA at anytime by clicking "Import Crossing Data from Selected Form on ONA". - -#### Reviewing Plant Status - -The mobile ODK application has options to collect information about the status of plants in the field, such as if they are flowering. Images for each plant can also be recorded. -The database will report this information here in a summary table that looks like the following. Notice that images are also transferred to the database. - -![]({{"assets/images/odk_ona_cross_status.png" | relative_url }}) - -#### Graphical Summary For Performed Crosses - -There is a section to summarize activities done for each cross. In this table each row represents a single cross performed. All the activities that have been performed will be shown here, such as "first pollination" and "embryo rescue". The scatter plot shown tracks seed numbers generated on the Y axis and date of activity on the X axis. - -![]({{"assets/images/odk_ona_cross_progress_graph.png" | relative_url }}) - -#### Summary Information For Performed Crosses - -There is a secondary section to summarize what has been done across the entire Cross Wishlist. This tree structure shows all activities performed for a cross and shows how these crosses relate to the Cross Wishlist. - -![]({{"assets/images/odk_ona_cross_progress_tree.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_15.md b/docs/03_managing_breeding_data/03_15.md deleted file mode 100644 index f2d1bf41f4..0000000000 --- a/docs/03_managing_breeding_data/03_15.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: "3.15 Managing Tissue Samples" -layout: doc_page ---- - - -* TOC -{:toc} - - -To access this page go to Manage and then Tissue Samples. - -### 3.15.1 Tissue samples from field trials - -A field trial contains plots planted with a specific accession. Each plot can contain many plants, which in turn can contain many tissue samples. -On the manage tissue sample page we can see the field trials that contain tissue samples already. We can choose to download the tissue sample layout as seen in the below picture. - -![]({{"assets/images/manage_tissue_samples_create_tissues_field_trial_download.png" | relative_url }}) - -If the field trial you want to collect tissue samples from is not in the above table, you can click the button highlighted below. - -![]({{"assets/images/manage_tissue_samples_create_tissues_field_trial_button.png" | relative_url }}) - -Once you have clicked this button, you will enter a workflow that begins with the following introduction. - -![]({{"assets/images/manage_tissue_samples_create_tissues_field_trial_intro.png" | relative_url }}) - -Once you click next, you will need to select your trial. - -![]({{"assets/images/manage_tissue_samples_create_tissues_field_trial_select.png" | relative_url }}) - -Next, if your trial currently only has plot entries saved, you will be asked to enter how many plants are in each plot. - -![]({{"assets/images/manage_tissue_samples_create_tissues_field_trial_num_plants.png" | relative_url }}) - -Finally you will be asked how many tissue samples you want for each plant. You can specify a string to include in the tissue sample name, such as leaf or root. - -![]({{"assets/images/manage_tissue_samples_create_tissues_field_trial_num_tissues.png" | relative_url }}) - -Afterwards you should see the following success message, indicating that the tissue samples are saved. - -![]({{"assets/images/manage_tissue_samples_create_tissues_field_trial_submit.png" | relative_url }}) - -### 3.15.2 Genotyping Plate Tissue Samples (96 or 384 well plates) - -A genotyping plate represents a 96 or 384 well plate. You can use the Coordinate Android application to create your plate layout, or you can upload your own Excel plate layout, or you can use the database to generate a plate layout. -Ideally, you will use tissue sample names originating from a field trial as the "source" for each well tissue sample, but you can also use plant names, plot names, or accession names. - -From the manage tissue samples page, you can see the genotyping plates saved in the database. You can also download the layouts as shown below. - -![]({{"assets/images/manage_tissue_samples_create_tissues_genotyping_trial_download.png" | relative_url }}) - -If you need to create a new genotyping plate, you can click the button shown below. This will guide you through a workflow for uploading or creating the new plate layout. - -![]({{"assets/images/manage_tissue_samples_create_tissues_genotyping_trial_button.png" | relative_url }}) - -Genotyping vendors require you to send a plate layout during submission. You can download the plate layout as shown above, or you can go to a genotyping plate detail page to download the Intertek formatted file. - -In the future you will be able to directly export your genotyping plate plate layout to vendors using the button below. - -![]({{"assets/images/manage_tissue_samples_create_tissues_genotyping_trial_export.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_16.md b/docs/03_managing_breeding_data/03_16.md deleted file mode 100644 index 35989f1c8e..0000000000 --- a/docs/03_managing_breeding_data/03_16.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: "3.16 Managing Observation Variables with Traits, Methods, and Scales" -layout: doc_page ---- - - -* TOC -{:toc} - - -### 3.16.1 Managing Observation Variables - -Observation variables are the identifiers used when collecting phenotypic data. An observation variable is composed of a trait, a method, and a scale. The trait describes the attribute being measured e.g. 'Plant Height'. The method defines the protocol in which the trait was observed e.g. 'Using a one meter long measuring stick'. The scale defines the units or dimensions for which the measurement was taken e.g. 'Meters'. - -Generally, observation variables are defined in ontologies that are predefined. We often use ontologies from cropontology.org. In this case, you will not be able to define your own observation variables directly; instead, you will need to contact us and we will add the observation variable for you. - -For databases where the user has greater control, we have an interface to allow addition of observation variables, along with traits, methods, and scales. To begin, go to the Search->Traits page. - -If the database you are on allows you to directly add observation variables, you will see the following button at the bottom of the page. - -![]({{"assets/images/manage_observation_variables_start_button.png" | relative_url }}) - -When you click the button, the following workflow will appear. You should be logged in or else it will not allow addition of the observation variable. -The workflow begins with an introduction. - -![]({{"assets/images/manage_observation_variables_workflow_intro.png" | relative_url }}) - -On the next workflow step, you select the ontology that you want to insert the new observation variable into. You must also give a name and a definition for the new observation variable. - -![]({{"assets/images/manage_observation_variables_workflow_observation_variable.png" | relative_url }}) - -On the next workflow step, you select the trait ontology to use. Once you select a trait ontology, a select containing all the terms in the selected ontology will appear. You can either select a trait or if it does not exist in the select, you can create a new one by giving a name and a definition for the new trait. - -![]({{"assets/images/manage_observation_variables_workflow_trait.png" | relative_url }}) - -On the next workflow step, you select the method ontology to use. Once you select a method ontology, a select containing all the terms in the selected ontology will appear. You can either select a method or if it does not exist in the select, you can create a new one by giving a name and a definition for the new method. - -![]({{"assets/images/manage_observation_variables_workflow_method.png" | relative_url }}) - -On the next workflow step, you select the scale ontology to use. Once you select a scale ontology, a select containing all the terms in the selected ontology will appear. You can either select a scale or if it does not exist in the select, you can create a new one by giving a name and a definition for the new scale. You can also define a format, minimum, maximum, categories, and default value for the new scale. - -![]({{"assets/images/manage_observation_variables_workflow_scale.png" | relative_url }}) - -On the last page of the workflow, you confirm the submission. - -![]({{"assets/images/manage_observation_variables_workflow_submit.png" | relative_url }}) - -Afterwards, you can use the newly created observation variable ontology term in your phenotyping. diff --git a/docs/03_managing_breeding_data/03_17.md b/docs/03_managing_breeding_data/03_17.md deleted file mode 100644 index 473b9ffdd6..0000000000 --- a/docs/03_managing_breeding_data/03_17.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: "3.17 Managing Image Data For Phenotyping" -layout: doc_page ---- - - -* TOC -{:toc} - - -### 3.17.1 Image-Phenotyping Dashboard - -![]({{"assets/images/manage_image_phenotyping_dashboard.png" | relative_url }}) - -(1) Upload raw image-captures in a compressed file (.zip) for orthophotomosaic assembly or upload previously stitched orthophotomosaic raster (.PNG, .JPG) imagery. (2) Dashboard shows all field trials and uploaded imaging events in collapsible sections. (3) Follow standard processes to manually create templates for assignment of plot-polygon images to the field experiment design. (4) All imagery is shown with the spectral category within collapsible sections. Figure shows NIR imagery. (5) Apply Fourier transform filtering, thresholding, and vegetation index masking. Plot-polygon images for all image processes are shown. (6) Extract and export phenotypic values from plot-polygon images for analyses and model training. - -### 3.17.2 Image Input - -Clicking "Upload Imagery" will open the following dialog. - -![]({{"assets/images/manage_image_phenotyping_upload_intro.png" | relative_url }}) - -Raw-captures can be uploaded in a compressed (.zip) file so that they can be assembled into an orthophotomosaic. If orthophotomosaic assembly is not required, raster images (.PNG, .JPG) can be uploaded. Example data is given for raw Micasense RedEdge 5-band multispectral captures and for stitched orthophotomosaics. - -![]({{"assets/images/manage_image_phenotyping_upload_field_trial.png" | relative_url }}) - -To begin uploading images, a field trial must be selected. The field trial must already be saved in the database. For information about adding a field trial, please read the Field Trial documentation. - -![]({{"assets/images/manage_image_phenotyping_upload_drone_run.png" | relative_url }}) - -The image data is added to an imaging (drone run) event. Here you can select a previously saved imaging event or you can create a new one by defining a name, description, and date. - -![]({{"assets/images/manage_image_phenotyping_upload_image_info_1.png" | relative_url }}) - -The uploaded data can be raw image-captures or complete raster images. Here you can select whether orthophotomosaic stitching is required. - -![]({{"assets/images/manage_image_phenotyping_upload_image_info_2.png" | relative_url }}) - -In the case that orthophotomosaic stitching is required, select 'yes'. On the next step you will see the following: -Upload a zipfile with the raw-captures. -When uploading Micasense RedEdge raw-captures, provide images of the Micasense calibration panels in a zipfile as well. - -![]({{"assets/images/manage_image_phenotyping_upload_images_zipfile.png" | relative_url }}) - -In the case that orthophotomosaic assembly is not required, simple upload the raster images. Select the number of image bands that will be uploaded e.g. for a five band multispectral camera, select 5. - -![]({{"assets/images/manage_image_phenotyping_upload_image_info_3.png" | relative_url }}) - -In the caes that orthophotomosaic stitching is not required, select 'no'. On the next step you will see the following: - -![]({{"assets/images/manage_image_phenotyping_upload_images_rasters.png" | relative_url }}) - -Upload an image at each band with a unique name, description, and spectral type. - -### 3.17.3 Standard Process - -Once imagery is uploaded, it will appear on the dashboard under the field trial. Clicking the "Run Standard Process" button will begin extracting plot-polygon phenotypes from the imagery. - -![]({{"assets/images/manage_image_phenotyping_standard_process_button.png" | relative_url }}) - -Clicking the button will open the following dialog. - -![]({{"assets/images/manage_image_phenotyping_standard_process_intro.png" | relative_url }}) - -Select a drone run band to use in this process. In the case of the Micasense 5 band multispectral camera there will be 5 bands shown here; select the NIR channel in this case because it has the highest contrast. In the case of standard color images, there will only be the RGB Color Image option here. - -![]({{"assets/images/manage_image_phenotyping_standard_process_drone_run_band.png" | relative_url }}) - -Rotate the image so that there the plots are oriented in a grid fashion. There can be a skew in the field layout, as seen in the following example. - -![]({{"assets/images/manage_image_phenotyping_standard_process_rotate.png" | relative_url }}) - -Perform a rough cropping of the image by clicking on the four corners of the field. Cropping is important to remove any extraneous parts of the image. - -![]({{"assets/images/manage_image_phenotyping_standard_process_cropping.png" | relative_url }}) - -This step shows a histogram of the cropped image. The standard process will magnitude threshold the top and low ends of the distribution. - -![]({{"assets/images/manage_image_phenotyping_standard_process_threshold_hist.png" | relative_url }}) - -In this step, the template for the plot polygons in the experimental field design are associated to the image. First, defined the number of rows and columns in the field experiment. Then click the four corners of the image, in respect to the top right, top left, botton left, and bottom right positions. Next click on "Draw Plot Polygon Template". Review the template and clear/repeat the process until the template matches well. It is possible to "copy/paste" templates in the case where there are large breaks in the field design. Next, scroll down to the "assign Plot Polygons to Field Trial Entities" section. Select the location of Plot Number 1 as either "top left" or "top right" and whether the field design is serpentine or zigzag. Click on "Generate Assignments" and review that the names of the plots appear correctly in the overlay on the image. Finally, click "Finish and Save Polygons to Plots" when you have have confirmed the assignments. - -![]({{"assets/images/manage_image_phenotyping_standard_process_plot_polygon.png" | relative_url }}) - -Next, the dialog shows you that the standard process will be repeated for all uploaded image bands. - -![]({{"assets/images/manage_image_phenotyping_standard_process_apply.png" | relative_url }}) - -Next, choose which vegetation indices to apply. - -![]({{"assets/images/manage_image_phenotyping_standard_process_vegetation_index.png" | relative_url }}) - -Next, choose the phenotypic values to extract. You must define the time point for which the phenotype is; if the field trial has a planting date, the time point will automatically be populated as image date minus the planting date. - -![]({{"assets/images/manage_image_phenotyping_standard_process_phenotypes.png" | relative_url }}) - -After completing the standard process, the job will continue in the background until it completes. You can check the status of the job from the dashboard. - -### 3.18.4 Ground Control Points - -Ground control points can be saved after an imaging event has undergone the standard process on orhomosaics. Ground control points can then be used across imaging events on the same field experiment in order to automate the entire standard process. diff --git a/docs/03_managing_breeding_data/03_18.md b/docs/03_managing_breeding_data/03_18.md deleted file mode 100644 index a782955582..0000000000 --- a/docs/03_managing_breeding_data/03_18.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: "3.18 Managing Genotypic Data in VCF" -layout: doc_page ---- - - -* TOC -{:toc} - - -### 3.18.1 Uploading Genotyping Data in VCF - -Genotyping data in VCF can be loaded from the web-interface. Breedbase can store any genotypic variants from a VCF, allowing for polyploids, structural variants, etc. without problems. - -To begin go to Manage->Genotyping Plates and click the button seen below: -Note that you do not need to have genotyping plates uploaded to upload VCF data; you may upload genotyping data to accessions or you can upload genotyping data for tissue samples in genotyping plates. - -![]({{"assets/images/manage_genotyping_data_upload_button.png" | relative_url }}) - -The workflow begins with an intro: - -![]({{"assets/images/manage_genotyping_data_upload_dialog_intro.png" | relative_url }}) - -On the following step in the workflow, a genotyping project is defined or selected. A genotyping project is a high-level entity for grouping several genotyping events. It is defined with a name, description, name, breeding program, and genotyping facility (IGD, Intertek, etc.). - -![]({{"assets/images/manage_genotyping_data_upload_dialog_project.png" | relative_url }}) - -The following step is to define or select a genotyping protocol. A genotyping protocol represents the set of markers being called against a specific reference genome. A genotyping protocol is defined with a name, description, reference genome name, species name, and a location of data generation. Note in the picture that you can select whether the samples in your file are accessions or tissue samples in the database; tissue samples are for when a genotyping plate is stored in the database. There is an option to parse the sample names for appended sequencing numbers from IGD, where the sample names are like "accession:igdnumber". - -![]({{"assets/images/manage_genotyping_data_upload_dialog_protocol.png" | relative_url }}) - -The final step is to select the VCF from your computer and upload it. The web interface can be used to upload files arbitrarily large; it is a NGINX configuration to set this size. - -![]({{"assets/images/manage_genotyping_data_upload_dialog_vcf.png" | relative_url }}) - -### 3.18.2 Searching and Downloading Genotyping Data in VCF Against Phenotyping Data - -The Search Wizard is the primary means of querying data in the database. Go to Search->Wizard to begin. - -Once genotyping protocols are stored, select Genotyping Protocols from the first dropdown menu. Then if you select one or more and select Accessions from the second dropdown menu, you will see the accessions for which genotypes were stored. As seen in the following picture, there is a section for filtering genotypes by chromosome, start position, and end position. Genotypes can be downloaded in VCF or DosageMatrix formats. - -![]({{"assets/images/manage_genotyping_data_wizard_download_prot.png" | relative_url }}) - -Using the "Default genotyping protocol" which is configured in a system, you can query over field phenotypic evaluations before downloading genotypes and phenotypes. - -![]({{"assets/images/manage_genotyping_data_wizard_trial.png" | relative_url }}) - -### 3.18.3 Searching Genotyping Protocols - -Genotyping protocols can be search by going to Search->Genotyping Protocols. To download genotypes accessions must be selected, though any combination of search criteria can be used to filter and select those accessions. If a genotyping protocol is not selected, then the default genotyping protocol set in the configuration will be used. Genotyping protocols can also be selected in the wizard. - -![]({{"assets/images/search_wizard_genotyping_protocols_download_main.png" | relative_url }}) - -The genotyping download menu on the Search Wizard presents options for filtering by chromosome, start position, and end position. Genotypes can be downloaded in VCF of Dosage Matrix formats. The genomic relationship matrix (GRM) can be downloaded for the selected accessions in a tab-delimited matrix format or in a three-column format that is useful in Asreml. Genotypes can be computed from the parents in the pedigree if those parents are genotyped by clicking on the "compute from parents" checkbox. Additionally, the GRM can be computed using genotypes of parents in the pedigree if the "compute from parents" checkbox is selected. - -![]({{"assets/images/search_wizard_genotyping_protocols_download_genotypes.png" | relative_url }}) - -As is described elsewhere, the Search Wizard presents a way to filter phenotypic values by minimum and maximum values, and allow for download in CSV and Excel formats. - -![]({{"assets/images/search_wizard_genotyping_protocols_download_phenotypes.png" | relative_url }}) - - -### 3.18.4 Genotyping Protocol Detail Page and Deleting a Genotyping Protocol - -The genotyping protocol detail page will show all information about the protocol such as the reference genome used, the header information lines in the uploaded VCF file, the markers involved, and the samples genotyped. - -The markers section will show all markers used and their annotations, such as position, chromosome, alternate allele, reference allele, marker format, etc. - -![]({{"assets/images/genotyping_protocol_detail_page_markers.png" | relative_url }}) - -The samples section will show all samples genotyped. Notice the Download links in the table which can be used to easily get the VCF file results for each genotyped samples with all markers in the genotyping protocol. For getting mulitple samples at once, use the Search Wizard as discussed above. - -![]({{"assets/images/genotyping_protocol_detail_page_samples.png" | relative_url }}) - -The genotyping protocol and all associated genotyping data can be deleted from the genotyping protocol page. - -![]({{"assets/images/genotyping_protocol_detail_page_delete.png" | relative_url }}) diff --git a/docs/03_managing_breeding_data/03_19.md b/docs/03_managing_breeding_data/03_19.md deleted file mode 100644 index fa345b6b0e..0000000000 --- a/docs/03_managing_breeding_data/03_19.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: "3.19 Managing Spectral Data" -layout: doc_page ---- - - -* TOC -{:toc} - - -Breedbase has implemented a flexible spectral data storage protocol that handles spectral data irrespective of the source spectrometer. Spectral data storage and analysis in Breedbase makes use of the R package *waves* for outlier identification, plotting, sample aggregation, and prediction model training. - - - ---- -### 3.19.1 Initiate upload of spectral data file -Spectral data can be added as a CSV file that includes metadata in the leftmost columns followed by one column per spectral measurement to the right. Rows represent a single scan or sample, each with a unique ID that must match to a Breedbase observationUnitName. Future data transfer using [BrAPI](https://brapi.org) will allow for interoperability with data collection software. - -To upload a spectral dataset, navigate to the 'Manage NIRS Data' page by selecting 'NIRS' in the 'Manage' menu and click the blue 'Upload NIRS' button. This will open an upload workflow. A link to the required file format and an example .csv file can be found by clicking in the light blue info box in this workflow. Another example of the file format is shown below. - -* **id**: Optional identifier for each NIRS read. The id must be an integer. -* **sampling_id**: Optional identifier for each sample. Strings are allowed. -* **sampling_date**: Optional field. The format allowed is: YYYY-MM-DD. -* **observationunit_name**: Required field that matches existing data in the database. It can be the plot name, subplots, plant name, or tissue sample, depending how your trial is designed. -* **device_id**: Optional field to identify your device. Strings are allowed. -* **device_type**: Required field. It is possible upload data for a single device type. They can be: SCiO, QST, Foss6500, BunchiN500, or LinkSquare. -* **comments**: Optional field for general comments. -All other columns are required wavelengths. You can add how many columns you want upload -- there is no limit. - - - - - -### 3.19.2 Evaluate and remove spectral outliers -Spectral calibration models can be heavily affected by the presence of outliers, whether they come from spectrometer spectral artifacts or user errors. Mahalanobis distance (Mahalanobis, 1936) is a measure of the distance between a single observation and a larger distribution and is commonly used in the identification of outliers in a multivariate space (Des Maesschalck et al, 2000). The *FilterSpectra()* function in the R package [*waves*](https://CRAN.R-project.org/package=waves) calculates the Mahalanobis distance of each observation in a given spectral matrix using the *stats::mahalanobis()* function. Observations are identified as outliers if the squared distance is greater than the 95th percentile of a $\chi$^2^-distribution with *p* degrees of freedom, where *p* is the number of columns (wavelengths) in the spectral matrix (Johnson and Wichern, 2007). In Breedbase, this procedure is applied on a per-dataset basis on upload and outliers are given binary tags "Outlier." - -### 3.19.3 Plot spectra with outliers highlighted -After outlier identification, a plot is generated using the *PlotSpectra()* function in [*waves*](https://CRAN.R-project.org/package=waves). This function uses the filtered spectra and *ggplot2::ggplot()* to create a line plot with outliers highlighted by color. A list of rows identified as outliers are shown beneath the plot. Plots are saved as .png files and linked to the original input datasets. Plot image files can be downloaded via the "Download Plot" button in the upload workflow. - - - -### 3.19.4 Aggregate spectra by observation units -To obtain a stable and reliable spectral profile, most spectrometer manufacturers recommend that multiple spectral scans are captured for each sample. While some spectrometers aggregate these scans internally, many do not, requiring the user to do so before analysis can take place. Breedbase handles these cases upon data upload following filtering steps by calling the *AggregateSpectra()* function from [*waves*](https://CRAN.R-project.org/package=waves), saving the aggregated scans for future access through the search wizard feature. Scans are aggregated by sample mean (e.g. plot-level basis) according to the provided observationUnitName field. After aggregation, the user exits the upload workflow and the raw data file is saved in the upload archive. - -### 3.19.5 References -* De Maesschalck, R., Jouan-Rimbaud, D., and Massart, D. L. (2000). The Mahalanobis distance. Chemom. Intell. Lab. Syst. 50(1): 1-18. -* Johnson, R. A. \& Wichern, D. W. (2007). Applied Multivariate Statistical Analysis (6th Edition). p 773. -* Mahalanobis, P. C. (1936). On the generalized distance in statistics. National Institute of Science of India. - -[**Analysis tool documentation**]({{site.baseurl}}{% link 04_data_analysis_tools.md%}#spectral-analysis) diff --git a/docs/03_managing_breeding_data/03_20.md b/docs/03_managing_breeding_data/03_20.md deleted file mode 100644 index 5425c815e7..0000000000 --- a/docs/03_managing_breeding_data/03_20.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "3.20 Managing User Roles" -layout: doc_page ---- - - -* TOC -{:toc} - - - - ---- -### 3.20.1 What are User Roles? - -Every user account in Breedbase has one or more associated "roles" that determine the authorizations (what the user is allowed to do) in the database. There are three fundamental roles, "curator", "submitter", and "user", which determine basic read/write levels. The "curator" status can read and write everything in the database. The "submitter" status can add information and edit or delete previously submitted information. The "user" type can only read data. Additional roles represent the breeding programs, and are sometimes used to fine-tune write and edit capabilities, as it necessary for multiple users in a breeding program to edit each other's data. - -### 3.20.2 The Manage User Roles page - -In the "Manage" menu, select the item "User Roles". This will show the current users in the database with their associated roles. If you are logged in as a curator, the table will show system roles as well as breeding program roles; if you are logged in as a submitter or user, it will show breeding program membership. - -If logged in as a "curator", the roles can be added or deleted. - - * To delete a role, click on the X in the role name. A confirm dialog will be displayed to prevent accidental deletion. - * To add a role, click on the plus sign next to the roles. A dialog will pop up with a list of roles. Select the desired role and click "Submit". - * The new role should be displayed next to the user immediately. - * Role deletions and additions will be effective immediately. - -It is recommended that few users be given the "curator" privileges to avoid confusion over data ownership and accidental data overwriting and deletion. - -[**Managing User Roles**]({{site.baseurl}}{% link 03_managing_breeding_data/03_20.md%}#managing_user_roles) diff --git a/docs/03_managing_breeding_data/03_21.md b/docs/03_managing_breeding_data/03_21.md deleted file mode 100644 index 27ba93fab6..0000000000 --- a/docs/03_managing_breeding_data/03_21.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: "3.21 Managing Sequence Metadata" -layout: doc_page ---- - - -* TOC -{:toc} - - -![]({{"assets/images/sequence_metadata_manage.png" | relative_url }}) - ---- - -### 3.21.1 What is Sequence Metadata? - -Sequence Metadata is a feature that allows for the efficient storage and retrieval of sequence annotations for a specific region along a reference genome. The annotation data can contain a primary "score" value and any number of secondary key/value attribute data. For example, Sequence Metatadata can store MNase open chromatin scores for every 10 basepairs along the reference genome as well as genome-wide association study (GWAS) statistics, including the trait information associated with the result. This data can then be filtered by position and/or scores/attribute values and even cross-referenced with markers stored in the database. - - -### 3.21.2 Loading Sequence Metadata - -Sequence Metadata can be loaded into the database using a gff3-formatted file. The following columns are used to load the data: - -- **#1 / seqid:** The name of the database feature (ie chromosome) the metadata is associated with (The feature name must already exist as a feature in the database) -- **#4 / start:** The metadata's start position -- **#5 / end:** The metadata's end position -- **#6 / score:** (optional) The primary score attribute of the metadata -- **#9 / attributes:** (optional) Secondary key//value attributes to be saved with the score. These should be formatted using the gff3 standard (key1=value1;key2=value2). The attribute key cannot be either score, start, or end. - -To upload the gff3 file: - -1. Go to the **Manage > Sequence Metadata** page -2. Click the **Upload Sequence Metadata** button -3. On Step 2 of the Wizard, select the Type of data to be uploaded - - This groups similar datasets together in the same Data Type category -4. On Step 3 of the Wizard, select an existing Protocol or create a new one - - The Protocol is used to describe how the data was generated and define the score value and any secondary attributes. Adding the attributes (and their descriptions) to the Protocol will allow the Sequence Metadata queries to filter the data based on the value of one or more of these attributes. Attributes not defined in the Protocol will still be stored and displayed on retrieval, but will not be able to be used in a search filter. -5. Finally, select and upload your gff3 file to the database. The database will verify the format of the file before its contents are stored. - - -### 3.21.2 Searching Sequence Metadata - -To retrieve stored Sequence Metadata, go to the **Search > Sequence Metadata** page. - - -#### Basic Search - -The basic Sequence Metadata search options include selecting the reference genome and species, the chromosome, and (optionally) the start and/or end position(s) along the reference genome. In addition, one or more specific protocols can be selected to limit the results. - -![]({{"assets/images/sequence_metadata_search_basic.png" | relative_url }}) - -The Sequence Metadata search results are returned as a table, including the chromosome and start/stop positions of the annotation, along with the primary score value and any additional key/value attributes. The markers column will include a list of marker names of any stored markers that are found within the start/stop positions of the Sequence Metadata. The data can be downloaded as a table in an Excel or CSV file or a machine-readable (code-friendly) JSON file. If the Sequence Metadata JBrowse configuration is set, the filtered results can be displayed as a dynamic JBrowse track. - -![]({{"assets/images/sequence_metadata_search_results.png" | relative_url }}) - -#### Advanced Search - -Any number of advanced search filters can be applied to the query. The advanced filters can limit the search results by the value of the primary score and/or any of the secondary attribute values. - -![]({{"assets/images/sequence_metadata_search_advanced.png" | relative_url }}) - - -### 3.21.3 Marker Integration - -A table of Sequence Metadata annotations are embedded on the Marker/Variant detail page. The table will include any annotations that span the poisiton of the marker (for data of the same reference genome and species). - -### 3.21.4 Sequence Metadata API - -A publicly accessible RESTful API (Application Programming Interface) is available to query the database for Sequence Metadata directly from your programming environment (R, python, etc) to be used in analysis. The data is returned in a JSON format. Documentation for the API can be found on the **Manage > Sequence Metadata** page diff --git a/docs/03_managing_breeding_data/03_22.md b/docs/03_managing_breeding_data/03_22.md deleted file mode 100644 index f923807874..0000000000 --- a/docs/03_managing_breeding_data/03_22.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: "3.22 Managing Outliers in Dataset" -layout: doc_page ---- - - -* TOC -{:toc} - - -[//]: # (![]({{"assets/images/sequence_metadata_manage.png" | relative_url }})) - ---- - -### 3.22.1 What is Outliers Functionality in Dataset ? - -![]({{"assets/images/outliers_dataset_icon.png" | relative_url }}) - -As in step **2.1.2 -> Saving the wizard selections** -we can create a dataset. - -The dataset incorporates a feature to identify outlier points, which we may choose to exclude from a specific dataset. It's important to note that these exclusions only apply at the dataset level, and no data is permanently removed from the database. Additionally, outlier categorization can be modified at any time, and these changes are visible to all other functionalities within the system. - -Each dataset stores a wholly unique set of outlier points, completely independent of any other dataset in the database. Outliers are specifically designated for traits within datasets, exclusively encompassing phenotype data. If a particular dataset lacks traits as a part of wizard selection, this functionality is not available. - -Each trait has its own set of defined outliers. - - - -### 3.22.2 Accessing Trait Visualisation - -Once you've selected a specific trait, the web application provides access to a visualisation of the data points associated with that trait. - -![]({{"assets/images/outliers_dataset_basic_panel.png" | relative_url }}) - -### 3.22.3 Interpreting Visual Elements - -Once you've selected a specific trait, the web application provides access to a visualisation of the data points associated with that trait. - -- **Green Points**: As per the legend, represent values for the selected trait that fall below the cut-off point set by the slider. (non-outliers) -- **Black Outlined Points**: These data points are outlined with black borders, indicating that they are currently designated as outliers in the database. -- **Red Points**: The red data points denote the cut-off points established by the slider for the allowable deviation value. - -![]({{"assets/images/outliers_dataset_full_visualisation.png" | relative_url }}) - -### 3.22.4 Choosing Cut-Off Values - -You have two fundamental options for setting cut-off points: - -- **Median with MAD**: This option involves using the median (middle value) along with the Mean Absolute Deviation (MAD) as a reference point for determining cut-off values. -- **Mean with Standard Deviation**: Alternatively, you can choose to use the mean (average) in conjunction with the Standard Deviation to set cut-off points. - -### 3.22.5 Setting Deviation Multiplier - -The slider allows you to specify the deviation multiplier from a central point, which influences the cut-off values. - -### 3.22.6 Utilising Graph Controls - -Beneath the graph, you'll find four buttons, each serving a distinct function: - -- **Add selection to outliers**: This button enables you to save the current cut-off points to the database for future reference. -- **Reset outliers for current trait**: You can use this option to reset outliers for the selected trait. -- **Reset all outliers**: This button allows you to reset outliers for the entire dataset. -- **Download Phenotype Table without outliers**: You can download the phenotype data table in a comma-separated value format file, using this feature, with outliers excluded for selected dataset. - -![]({{"assets/images/outliers_dataset_actions.png" | relative_url }}) - -These tools and functions are designed to provide you with control and insights when working with data visualisation and outliers.. diff --git a/docs/03_managing_breeding_data/index.md b/docs/03_managing_breeding_data/index.md deleted file mode 100644 index 8a25ba8b0f..0000000000 --- a/docs/03_managing_breeding_data/index.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "3. Managing Breeding Data" -layout: doc_folder ---- - -Breeding data can be submitted and managed by using “Manage” section. In order to submit and manage breeding data on the database, you need to login and your account must have “submitter” status. - -![]({{"assets/images/image90.png" | relative_url }}) diff --git a/docs/04_data_analysis_tools.md b/docs/04_data_analysis_tools.md deleted file mode 100644 index 6c5e3ab418..0000000000 --- a/docs/04_data_analysis_tools.md +++ /dev/null @@ -1,510 +0,0 @@ ---- -title: "4. Data Analysis Tools" -layout: doc_page ---- - - -* TOC -{:toc} - - -SGN databases provides several tools for phenotype data analysis, marker-assisted selection, sequence and expression analyses, as well as ontology browser. These tools can be found in the “Analyze” menu. - -![]({{"assets/images/image114.png" | relative_url }}) - -4.1 Selection Index -------------------- - -To determine rankings of accessions based on more than one desirable trait, SGN databases provide a “Selection Index” tool that allows you to specify a weighting on each trait. To access the tool, clicking on “Selection Index” in the “Analyze” menu. - -![]({{"assets/images/image251.png" | relative_url }}) - -On the Selection Index page, selecting a trial that you want to analyze. - -![]({{"assets/images/image95.png" | relative_url }}) - -After you selected a trial, you can find traits that were assayed in that trial in the “Trait” box. - -![]({{"assets/images/image78.png" | relative_url }}) - -Selecting a trait that you want to include in the analysis will open a new dialogue showing the selected trait and a box that you can assign a “Weight” of that trait. After you are done, you can continue by selecting another trait by clicking on “Add another trait” link. - -![]({{"assets/images/image304.png" | relative_url }}) - -After you selected another trait, this page will automatically update information for you by showing all of the traits that you selected for the analysis. - -![]({{"assets/images/image76.png" | relative_url }}) - -You also have options to choose a reference accession, choose to include accessions with missing phenotypes, scaling values to a reference accession. After you complete your setting, clicking on “Calculate Rankings” - -![]({{"assets/images/image343.png" | relative_url }}) - -The Selection Index tool will generate rankings of accessions based on the information that you specified. You can copy the results to your system clipboard, convert the table data to CSV format, or print the data. - -![]({{"assets/images/image326.png" | relative_url }}) - -Clicking on “Raw Average” will display average values of the phenotypes of those ranked accessions. - -![]({{"assets/images/image150.png" | relative_url }}) - -Selection Index tool also allows you to save top ranked accessions directly to “Lists”. You can retrieve top ranked accessions by selecting a number or a percent. - -![]({{"assets/images/image156.png" | relative_url }}) - -4.2 Genomic Selection ---------------------- - -The prediction of breeding values for a trait is a one step or two steps process, depending on what stage in your breeding cycle you are. The first step is to build a prediction model for a trait using a training population of clones with phenotype and genotype data. If you have yet to select parents for crossing for your first cycle of selection you can use the breeding values of the training population. If you are at later stages of your selection program, you need to do the second step which is applying the prediction model on your selection population. All clones in your training and selection populations must exist in the database. - -To use the genomic selection tool, on [*cassavabase.org*](http://cassavabase.org/), select 'Genomic Selection' from the 'analyze' pull-down menu. - -![]({{"assets/images/image247.png" | relative_url }}) - -### 4.2.1 Building a prediction model - -There are three ways to build a model for a trait. - -#### Method 1: {#method-1} - -One way to build a model is, using a trait name, to search for trials in which the trait was phenotyped and use a trial or a combination of trials to build a model for the trait. For example, if you search for 'mosaic disease severity, you will get a list of trials you can use as training populations. - -![]({{"assets/images/image160.png" | relative_url }}) - -You will get a list of trials (as shown below) in which the trait of your interested was phenotyped. From the list, you can use a single trial as a training population or combine several trails to form a training population for the prediction model of the trait. Let's say, you want to create a training population using individuals from trials 'cassava ibadan 2001/02' and 'cassava ibadan 02/03' and build a model for 'cassava mosaic disease severity' using all clones from the training population. - -![]({{"assets/images/image249.png" | relative_url }}) - -Select the trials to combine (the same coloured), click ‘done selecting’, click the 'combine trials and build model' button, and you will get a model and its output for the trait. On the model detail page, you can view the description of input data used in the model, output from the model and search interface for selection populations the model you can apply to predict their breeding values. The description of the input data for the model includes the number of phenotyped clones, and the number of markers, scatter and frequency distribution plots for the phenotype data, relationship between the phenotype data and GEBVs, population structure. The model output includes model parameters, heritability of the trait , prediction accuracy, GEBVs of the individuals from the training population and marker effects. - -![]({{"assets/images/image330.png" | relative_url }}) - -Expand each section to see detailed information. - -If you expand the ‘Trait phenotype data’ section, you will find plots to explore the phenotype data used in the model. You can assess the phenotype data using a scatter and histogram plots and the descriptive statistics. - - - - - -A regression line between observed phenotypes and GEBVs shows the relationship between the two. - -![]({{"assets/images/image83.png" | relative_url }}) - -You can also explore if there is any sub-clustering in the training population using PCA. - - - -To check the model accuracy, a 10-fold cross-validation test, expand the ‘model accuracy’ section. - - - -Marker effects are also available for download. To do so, expanad the ‘Marker Effects’ section and click the ‘Download all marker effects’ link and you will get a tab delimited output to save on your computer. - - - -The breeding values of the individuals used in the training population are displayed graphically. Mousing over each data point displays the clone and its breeding value. To examine better, you can zoom in into the plot by selecting an area on the plot. You can download them also by following the 'Download all GEBVs' link. - - - - -##### Estimating breeding values in a selection population - -If you already have a selection population (in the database), from the same model page, you can apply the model to the selection population and estimate breeding values for all the clones in the population. You can search for a selection population of clones in the database using the search interface or you can make a custom list of clones using the [*list interface*]({{site.baseurl}}{% link 01_basic_website_usage.md%}#working-with-lists). If you click the 'search for all relevant selection populations', you will see all relevant selection populations for that model. However, this option takes long time decause of the large set of populations in the database and the filtering. Therefore, the fastest way is to search for each of your selection populations by name. If you are logged in to the website you will also see a list of your custom set of genotyped clones. - -![]({{"assets/images/image338.png" | relative_url }}) - -To apply the model to a selection population, simply click your population name or 'Predict Now' and you will get the predicted breeding values. When you see a name of (or acronym\]) of the trait, follow the link and you will see an interactive plot of the breeding values and a link to download the breeding values of your selection population. - - - -#### Method 2 {#method-2} - -Another way to build a model is by selecting a trial, instead of selecting and searching for a specific trait. This approach is useful when you know a particular trial that is relevant to the environment you are targeting to breed material for. This method allows you to build models and predict genomic estimated breeding values (GEBVs) for several traits within a single trial at once. You can also calculate selection index for your clones when GEBVs are estimated for multiple traits. - -To do this select the "Genomic Selection" link found under the "analyze" menu. This will take you to the same home page as used with Method 1. However, instead of entering information to search for in "Search for a trait", click on "Use a trait as a trial population". This will expand a new menu that will show all available trials. - -![]({{"assets/images/image344.png" | relative_url }}) - -arrow.png - -![]({{"assets/images/image341.png" | relative_url }}) - -To begin creating the model, select the existing trial that you would like to use. In this example I will be using the trial and trait data from "Cassava Ibadan 2002/03" trial. Clicking on a trial will take you to a page where you can find information such as number of markers and number of phenotypes clones. - -![]({{"assets/images/image322.png" | relative_url }}) - -In addition to the number of phenotype clones and number of markers, the main page for the trial selected also has information and graphs on phenotypic correlation for all of the traits. By moving your cursor over the graph you can read the different values for correlation between two traits. A key with all of the trait names of the acronyms used can be found in the tab below the graph. - -![]({{"assets/images/image151.png" | relative_url }}) - -Below the "Training population summary" there is a tab for "Traits". Clicking on this tab will show all available traits for the specific trial. You can create a model by choosing one or multiple traits in the trial and clicking "Build Model". In this example, the traits for "cassava bacterial blight severity" and "cassava mosaic disease severity" have been selected. - -![]({{"assets/images/image69.png" | relative_url }}) - -Clicking on 'Build Model' will take you to a new page with the models outputs for the traits. Under the "Genomic Selection Model Output" tab you can view the model output and the model accuracy. Clicking on any of the traits will take you to a page with information about the model output on that individual trait within the trial. There you can view all of the trait information that was seen in more detail in [*Method 1*](#method-1). - -![]({{"assets/images/image336.png" | relative_url }}) - -You can apply the models to simultaneously predict GEBVs for respective traits in a selection population by clicking on "Predict Now" or the name of the selection population. You can also apply the models to any set of genotyped clones that you can create using the 'lists' feature. For more information on lists, click [*here*]({{site.baseurl}}{% link 01_basic_website_usage.md%}#working-with-lists). Follow the link to the trait name to view and download the predicted GEBVs for the trait in a selection population. - - - -To compare clones based on their performance on multiple traits, you can calculate selection indices using the form below. Choose from the pulldown menu the population with predicted GEBVs for the traits and assign relative weights for each trait. The relative weight of each trait must be between 0 - 1. 0 being of least weight and importance, not wanting to consider that particular trait in selecting a genotype and 1 being a trait that you give highest importance. - -In this example we will be using the "Cassava Ibadan 2002/03" population and assigning values to each of the traits. Remember that there is a list of acronyms and trait names at the bottom of the page for reference. After entering whatever values you would like for each trait click on the "Calculate" button to generate results. This will create a list of the top 10 genotypes that most closely match the criteria that you entered. The list will be displayed right below the "selection index" tab. This information can also be downloaded onto your computer by clicking on the "Download selection indices" link underneath the listed genotypes and selection indices. - - - -#### Method 3 - -In addition to creating a model by searching for pre-existing traits or by preexisting trial name, models can also be created by using your own list of clones. This creates a model by using or creating a training population. - -The page to use the third Method for creating a population model is the same as for the other two models. Select "Genomic Selection" from under the "analyze" menu of the main toolbar. This will take you to the Genomic Selection homepage and show you all three available methods to create a model. To see and use Method 3 scroll down and click on the tab labeled "Create a Training Population". This will open a set of tools that will allow you to use pre-existing lists or to create a new list. - -![]({{"assets/images/image138.png" | relative_url }}) - -Once the "Create a Training Population" tab is opened you have the option to use a pre-existing list or create new one. To learn how to create a list, click [*here*]({{site.baseurl}}{% link 01_basic_website_usage.md %}#working-with-lists). The "Make a new list of plots" link will take you directly to the Search Wizard that is usually used to create lists. - -Please note: the only lists that can be used in Method 3 to create a model are lists of plots and trials. If the pre-existing list is not of plots or trials (for example, traits, or locations) it will not show up and cannot be used as a training population. When you create you use a list of trials, the trials data will be combined to create a training data set. - -To use your custom list of plots or trials as a training population, select the list and click "Go". This will take you to a detail page for the training population. - -![]({{"assets/images/image181.png" | relative_url }}) - -From here on you can build models and predict breeding values as described in [*Method 2*](#method-2)**.** - -4.3 Genome Browsing -------------------- - -There are two ways to evaluate genotype information within the browser, from an accession detail page or a trial detail page. - -### 4.3.1 Browsing Genotype data by Accession - -If you are interested in browsing genotype information for a single accession, for example ‘BAHKYEHEMAA’, navigate to the accession detail page. - - - -Near the bottom of the detail page is a collapsible section called “Accession Jbrowse”. - -This section will contain a link to the accession jbrowse page if the necessary genotype data is available. Clicking the link should take you to a page that looks like this, a which point you can browsre the genotype data in the form of a vcf track aligned to the latest build of the genome. - -![]({{"assets/images/image318.png" | relative_url }}) - -### 4.3.2 Browsing Genotype data by Trial - -If you are interested in browsing genotype information for the accessions within a given trial, navigate to the trial detail page. - -Halfway down the page is a collapsible section called “Trial Jbrowse”. This section will contain a link to the trial jbrowse page if the necessary genotype data for at least two accessions planted in the trial is available. - - - -Clicking the link should take you to a page that looks like this, a which point you can browse the genotype data in the form of vcf tracks aligned to the latest build of the genome.![]({{"assets/images/image327.png" | relative_url }}) - -4.4 Principal Component Analysis (PCA) --------------------------------- - -Principal component analysis helps estimate and visualize if there is sub-grouping of individuals within a dataset based on a number of variables. Currently, you can use marker data to run PCA on datasets. - -You can run PCA from multiple places on the website. To do PCA on - -(1) individuals from a trial, go to the trial detail page and find the PCA tool under the 'Analysis tools' section. - -(2) individuals from a training population you used in a GS modeling, do your modeling and find the PCA tool in the model output page. - -(3) individuals in a training population and selection population you applied the training model, do your modeling, apply the model on the selection population and find the PCA tool on the selection population prediction output page. - -(4) individuals in a list of accessions you created, for example using the search wizard, go to the 'Analyze' menu and select the 'Population Structure', select your list of individuals and run PCA. - -(5) individuals from multiple trials, create a list of the trials using the search wizard, go to the 'Analyze' menu and select the 'Population Structure', select your list of trials and run PCA. - - - -With all the options, you will get a interactive plot of the two PCs (shown below) that explain the largest variance. Point the cursor at any data point and you will see the individual name with its corresponding PCs scores. By clicking the ‘Download all PCs’, you can also download the 10 PCs scores in the text format. - - -4.5 ANOVA --------------- - -Currently, ANOVA is implemented for a single trial (single year and single location). You can do ANOVA for RCBD, CRD, Alpha and Augmented trial designs. ANOVA is done using linear mixed effects model, where the genotypes is fixed effect and the replications and blocks are random effects. Fixed effect significance level is computed using 'lmer' from 'lmeTest' R package. - -You can do ANOVA from two places: trial detail and training population detail. In both cases, if the phenotype data was from the supported trial designs, - --- Go to the ANOVA section down in the trial or training population page - --- Select the trait of you want to perform ANOVA - --- Click the 'Run ANOVA' and wait for the result - - - -4.6 Clustering (K-Means, Hierarchical) --------------- -The K-Means method allows you to partition a dataset into groups (K number). The hierarchical clustering, agglomerative, allows you to explore underlying similarity and visualize in a tree structure (dendrogram) the different levels of similarities (clusters) among samples. You can do clustering based on marker data, phenotype data and GEBVs. When you use phenotype data, first clone averages for each trait are calculated. Both methods use Euclidean distance as a measure of similarity. For the hierachical clustering, the complete-linkage (farthest neighbour) method is used to link up clusters. - -There are three pathways to using this tool. - -(1) When you have data in the form of a list or dataset from the search wizard: - - (A) -- go to the 'Analyze' menu and select the clustering option - - (B) -- make sure you are logged in - - (C) -- Select the relevant genotyping protocol, if you are clustering using genotype data - - (D) -- select your list or dataset, click 'Go' - - (E) -- select clustering type - - (F) -- select the data type to use - - (G) -- If you are running K-Means clustering, provide the number of partitions (K). If left blank it will partition the data set into optimal numbers for the dataset. - - (H) -- click the 'Run Cluster' and wait for the analysis to finish or queue the request and wait for an email with the analysis result. - - (I) -- You can download the outputs following the download links. - -(2) From the trial detail page: - - (A) -- Go to the 'Analysis Tools' section - - (B) -- Follow steps D to G in (1) - -(3) In the solGS pipeline: - - (A) -- Once you you are in a model output put page, you will see a section where you can do clustering in the same way as above (option 2). - -K-Means clustering: - - - -Hierarchical clustering: - - - -4.7 Genetic Gain --------------- - -You can check for genetic gain by comparing the the GEBVs of a training and a selection population. You can do this in the solGS pipepline once you build a model and apply the model to predict the GEBVs of a selection population. Once at that stage, you will see a section 'Check Genetic Gain'. Select a selection population to compare with the training population and click the 'Check Genetic Gain' button. The genetic gain will be visualized in boxplots. You can download the boxplot(s) as well as the GEBVs data used for the plot(s). - - - - -4.8 Kinship and Inbreeding Coefficients --------------- -This tool allows you to estimate genetic relatedness between a pair of individuals (kinship), homozygousity across loci in an individual (inbreeding coefficient), and genetic similarity of an individual relative to the rest of the population (averge kinship). - -There are three pathways to using this tool. - - (1) When you have a list or dataset clones, created from the search wizard: - - (A) -- go to the 'Analyze' menu and select the kinship and inbreeding - - (B) -- make sure you are logged in - - (C) -- Select the genotypic protocol for the marker data - - (D) -- select your list or dataset of clones, click 'Go' - - (F) -- click the 'Run Kinship' and wait for the analysis to finish, depending on the data size this may take minutes. You can choose to submit the analysis and wait for an email notice to view the results or wait for it to complete. - - (G) -- You can download the output following the download links. - -(2) From the trial detail page: - - (A) -- Go to the 'Analysis Tools' section - - (B) -- Follow steps C to G in (1) - -(3) In the solGS pipeline: - - (A) -- Once you you are in a model output put page, scroll down to the 'Kinship and Inbreeding' section and run kinship. - - - - - - - -4.9 Creating Crossing Groups --------------- - -If you calculate selection index based on GEBVs of multiple traits, and you want to select a certain proportion of the indexed individuals (e.g. top 10%, or bottom 10%) and then you want to partition the selected individuals into a number of groups based on their genotypes, you can use the k-means clustering method. - -The procedure is: - -(1) predict GEBVs for 2 or more traits - -(2) In the models output page, calculate selection indices. Note the name of the selection index data. - -(3) Go to the clustering section, - - -- select the selection index data, - - -- select 'K-means', - - -- select 'Genotype', - - -- in the K-numbers textbox, fill in the number of groups you want to create, - - -- in the selection proportion textbox, fill in the proportion of the indexed individuals you want to select, e.g. for the top 15 percent, 15. if you wish to select bottom performing, prefix the number with minus sign (e.g. -15) - - -- then run cluster and wait for the result. - - - -4.10 Search Wizard Genomic Relationship Matrix (GRM) Download --------------- - -The genomic relationship matrix (GRM) is useful for understanding underlying structure in your population. Breedbase can compute the GRM using rrBLUP. First, select accessions in the search wizard and optionally select a genotyping protocol. If no genotyping protocol is selected, the default genotyping protocol in your system is used (as defined in sgn_local.conf). Specify the minor allele frequency, missing marker data, and missing individuals data filters to apply. The GRM can be returned in a matrix format (.tsv) which shows all pairwise relationships between the selected accessions and is useful for visualization; alternatively, the GRM can be returned in a three-column format (.tsv) which is useful for programs like ASReml outside of Breedbase. The GRM can also be returned as a simple correlation heatmap image (.pdf). The GRM can be computed from parents of the selected accessions granted the parents were genotyped, by clicking the checkbox "compute from parents"; this is useful for programs where parental lines are genotyped and then hybrids are created and evaluated in the field. - - - - -4.11 Search Wizard Genome Wide Association Study (GWAS) --------------- - -Performing a genome wide association study (GWAS) can determine genotypic markers which are significantly correlated to phenotypic traits. Breedbase can compute GWAS using rrBLUP. First, select accessions and trait(s) in the search wizard, and optionally select a genotyping protocol. If no genotyping protocol is selected, the default genotyping protocol in your system is used (as defined in sgn_local.conf). Several traits can be selected in the search wizard; if the traits are not to be treated as repeated measurements then select 'no' in the select box and this will tell Breedbase to return GWAS results independently for the selected traits. If the selected traits are indeed all repeated measurements then select 'yes' in the select box and Breedbase will return as single GWAS analysis across all the phenotypic records. Specify the minor allele frequency, missing marker data, and missing individuals data filters to apply. GWAS results can be returned in a tabular format (.tsv) where the -log10(p-values) for the selected traits are returned; alternatively, the GWAS results can be returned as Manhattan and QQ plots for the selected traits. The GWAS can be computed from parents of the selected accessions granted the parents were genotyped, by clicking the checkbox "compute from parents"; this is useful for programs where parental lines are genotyped and then hybrids are created and evaluated in the field. - -The GWAS will filter the data by the input MAF and missing data filters provided. After filtering the data is imputed using an 'EM' method in rrBLUP. The Kinship matrix (GRM) is computed from the imputed genotypic data and used in the GWAS model. The GWAS uses fixed effects for different field trials and replicates in the phenotypic data. - - - - - - - - -4.12 Spectral Analysis {#spectral-analysis} --------------- - -Visible and near-infrared spectroscopy (vis-NIRS) can be related to reference phenotypes through statistical models to produce accurate phenotypic predictions for unobserved samples, increasing phenotyping throughput. This technique is commonly used for predicting traits such as total starch, protein, carotenoid, and water content in many plant breeding programs. Breedbase implements the R package [*waves*](https://CRAN.R-project.org/package=waves) to offer training, evaluation, storage, and use of vis-NIRS prediction models for a wide range of spectrometers and phenotypes. - - - -### Dataset selection -In order to initiate an analysis, the user must select one or more datasets using the Breedbase [**Search Wizard**]({{site.baseurl}}{% link 02_searching_the_database.md%}#the-search-wizard). A dataset in Breedbase can contain observationUnit-level (plot-, plant-, or sample-level) trial metadata and phenotypic data from one or more trials. After navigating to the “NIRS” webpage under the “Manage” tab in Breedbase, the user can initiate an analysis and select one of these datasets as input for model training. An optional test dataset can be selected in the second step of the workflow. - - - - - -### Cross-validation -Five cross-validation schemes that represent scenarios common in plant breeding are available for this analysis. These include CV1, CV2, CV0, and CV00 as outlined below and described in depth by Jarquín et al. (2017) as well as random and stratified random sampling with a 70% training and 30% validation split. For those schemes from Jarquín et al. (2017), specific input datasets must be chosen based on genotype and environment relatedness. Cross-validation choices: -* **Random sampling** (70% training / 30% validation) -* **Stratified random sampling**, stratified based on phenotype (70% training / 30% validation) -* **CV1**, untested lines in tested environments -* **CV2**, tested lines in tested environments -* **CV0**, tested lines in untested environments -* **CV00**, untested lines in untested environments - - - -### Preprocessing -Preprocessing, also known as pretreatment, is often used to increase the signal to noise ratio in vis-NIR datasets. The *waves* function *DoPreprocessing()* applies functions from the *stats* and *prospectr* packages for common spectral preprocessing methods with the following options: -* Raw data (default) -* First derivative -* Second derivative -* Gap segment derivative -* Standard normal variate (SNV; Barnes et al., 1989) -* Savitzky-Golay polynomial smoothing (Savitzky and Golay, 1964) - -For more information on preprocessing methods and implementation, see the [*waves*](https://CRAN.R-project.org/package=waves) manual, available through CRAN: [waves.pdf](https://cran.r-project.org/web/packages/waves/waves.pdf) - - - -### Algorithms -Several algorithms are available for calibration model development in Breedbase via the [*waves*](https://CRAN.R-project.org/package=waves) package. The *TrainSpectralModel()* function in waves performs hyperparameter tuning as applicable using these algorithms in combination with cross validation and train functions from the package *caret*. Currently, only regression algorithms are available, but classification algorithms such as PLS-DA and SVM clasification are under development. -* **Partial least squares regression** (PLSR; Wold et al., 1982; Wold et al., 1984) is a popular method for spectral calibrations, as it can handle datasets with high levels of collinearity, reducing the dimensionality of these data into orthogonal latent variables (components) that are then related to the response variable through a linear model (reviewed in Wold et al., 2001). To avoid overfitting, the number of these components included in the final model must be tuned for each use case. The PLSR algorithm from the *pls* package is implemented by waves. -* **Random Forest regression** (RF; Ho, 1995) is a machine learning algorithm based on a series of decision trees. The number of trees and decisions at each junction are hyperparameters that must be tuned for each model. Another feature of this algorithm is the ability to extract variable importance measures from a fitted model (Breiman, 2001). In Breedbase, this option is made available through implementation of the RF algorithm from the package randomForest in the waves function TrainSpectralModel(). This function outputs both model performance statistics and a downloadable table of importance values for each wavelength. It is worth noting that this algorithm is computationally intensive, so the user should not be alarmed if results do not come right away. Breedbase will continue to work in the background and will display results when the analysis is finished. -* **Support vector machine regression** (SVM; Vapnik, 2000) is another useful algorithm for working with high-dimension datasets consisting of non-linear data, with applications in both classification and regression. The package waves implements SVM with both linear and radial basis function kernels using the kernlab package. - -### Output: common model summary statistics -After training, model performance statistics are both displayed on a results webpage and made available for download in .csv format. These statistics are calculated by the *TrainSpectralModel()* function in [*waves*](https://CRAN.R-project.org/package=waves) using the *caret* and *spectacles* packages. Reported statistics include: -* Tuned parameters depending on the model algoritm - * **Best.n.comp**, the best number of components to be included in a PLSR model - * **Best.ntree**, the best number of trees in an RF model - * **Best.mtry**, the best number of variables to include at every decision point in an RF model -* **RMSECV**, the root mean squared error of cross-validation -* **R2cv**, the coefficient of multiple determination of cross-validation for PLSR models -* **RMSEP**, the root mean squared error of prediction -* **R2p**, the squared Pearson’s correlation between predicted and observed test set values -* **RPD**, the ratio of standard deviation of observed test set values to RMSEP -* **RPIQ**, the ratio of performance to interquartile distance -* **CCC**, the concordance correlation coefficient -* **Bias**, the average difference between the predicted and observed values -* **SEP**, the standard error of prediction -* **R2sp**, the squared Spearman's rank correlation between predicted and observed test set values - -### Export model for later use -Once a model has been trained, it can be stored for later use. This action calls the *SaveModel()* function from [*waves*](https://CRAN.R-project.org/package=waves). Metadata regarding the training dataset and other parameters specified by the user upon training initialization are stored alongside the model object itself in the database. - - - -### Predict phenotypes from an exported model (routine use) -For phenotype predictions, users select a dataset and can then choose from models in the database that were trained using the same spectrometer type as the spectral data in the chosen dataset. Predicted phenotypes are stored as such in the database and are tagged with an ontology term specifying that they are predicted and not directly measured. Metadata regarding the model used for prediction is stored alongside the predicted value in the database. Predicted phenotypes can then be used as normal in other Breedbase analysis tools such as the Selection Index and GWAS. - - - - - -### FAQ -The Breedbase Spectral Analysis Tool does not allow for prediction models involving data from multiple spectrometer types at once. - -References -* Barnes, R.J., M.S. Dhanoa, and S.J. Lister. 1989. Standard normal variate transformation and de-trending of near-infrared diffuse reflectance spectra. Appl. Spectrosc. 43(5): 772-777. doi: 10.1366/0003702894202201. -* Breiman, L. 2001. Random forests. Mach. Learn. 45: 5-32. doi: 10.1201/9780429469275-8. -* Ho, T.K. 1995. Random decision forests. Proc. Int. Conf. Doc. Anal. Recognition, ICDAR 1: 278-282. doi: 10.1109/ICDAR.1995.598994. -* Jarquín, D., C. Lemes da Silva, R.C. Gaynor, J. Poland, A. Fritz, et al. 2017. Increasing Genomic-Enabled Prediction Accuracy by Modeling Genotype x Environment Interactions in Kansas Wheat. Plant Genome 10(2): plantgenome2016.12.0130. doi: 10.3835/plantgenome2016.12.0130. -* Johnson, R.A., and D.W. Wichern. 2007. Applied Multivariate Statistical Analysis (6th Edition). -De Maesschalck, R., D. Jouan-Rimbaud, and D.L. Massart. 2000. The Mahalanobis distance. Chemom. Intell. Lab. Syst. 50(1): 1-18. doi: 10.1016/S0169-7439(99)00047-7. -* Mahalanobis, P.C. 1936. On the generalized distance in statistics. Natl. Inst. Sci. India. -* Savitzky, A., and M.J.E. Golay. 1964. Smoothing and Differentiation of Data by Simplified Least Squares Procedures. Anal. Chem. 36(8): 1627-1639. doi: 10.1021/ac60214a047. -* Shrestha, R., L. Matteis, M. Skofic, A. Portugal, G. McLaren, et al. 2012. Bridging the phenotypic and genetic data useful for integrated breeding through a data annotation using the Crop Ontology developed by the crop communities of practice. Front. Physiol. 3 AUG(August): 1-10. doi: 10.3389/fphys.2012.00326. -* Vapnik, V.N. 2000. The Nature of Statistical Learning Theory. Springer New York, New York, NY. -* Wold, S., A. Ruhe, H. Wold, and W.J. Dunn, III. 1984. The Collinearity Problem in Linear Regression. The Partial Least Squares (PLS) Approach to Generalized Inverses. SIAM J. Sci. Stat. Comput. 5(3): 735-743. doi: 10.1137/0905052. -* Wold, S., M. Sjöström, and L. Eriksson. 2001. PLS-regression: a basic tool of chemometrics. Chemom. Intell. Lab. Syst. 58(2): 109-130. doi: 10.1016/S0169-7439(01)00155-1. - - -4.13 General Mixed Model Tool --------------- - -The general mixed model tool is available at /tools/mixedmodels and a link is provided from the Analyze menu. - -To use the mixed model tool, first create dataset using the Wizard containing the data that you would like to analyze. - -Select the Mixed Model tool from the Analyze menu. - -You are presented with a workflow. On the first step of the workflow, select the dataset that you wish to analyze, click on "Choose dataset" to continue. - -The second part of the workflow presents you with the traits in the dataset; you can select one or more traits from the lists using the select buttons. If you selected one trait, a bargraph of the trait distribution will be shown. Click the "Next step" button to move to the next screen. - - - -On the model build screen, all the factors are displayed that are contained within the dataset. The factors are presented as a list of blue buttons that can be dragged using the mouse to areas on the screen which build a mixed model equation. The areas correspond to fixed factors, random factors, and optionally to more complex factors, such as fixed factors with interaction and fixe factors with vriable slope/intersects. Drag the available factors to the corresponding area. To calculate BLUPs for germplasm, drag the germplasmName button to the "Random factors" area. To calculate BLUEs, drag it to the "Fixed factors" area. The factors need to have different levels contained within them, for example, if there is only one trial in the dataset, it cannot be used as one of the factors. Click on "Run analysis and got to next step" to run the mixed model and display the results. - -The result view contains two tabs, one with the raw data, either BLUPS or BLUEs, and the other the adjusted means from the raw data. - -The results can be stored in the database as an analysis, by clicking the button provided on the top of the data. - - -4.14 Genomic Prediction of Cross Performance (GCPC) --------------- - -The GCPC tool is available at /tools/gcpc and a link is provided from the Analyze menu. -The GCPC tool implements genomic prediction with additive and directional dominance in the linear mixed model to predict for cross performance. - -Before using the tool, first create a dataset using the Wizard containing the data that you would like to analyze. (The dataset should have genotyping_protocols). -Second, create Selection Indices for your traits using Selection Index in Analyze Menu. - -To use the tool, Select the GCPC tool from the Analyze menu. - -Then, select the dataset with genotyping_protocols that you wish to analyze, click on "Proceed to Factor Selection" to load available factors that can be included in the model. - -Select the factors you wish to include in the model either as Fixed or Random. Click "None" for factors that you don't want to include in the model. Note that the "germplasmName" is factored as Random by default. - -The next step is to select the selection index for your traits on the dropdown menu. - -Once you are through, click "Run GCPC" to run the model. The output will be presented in form of a table with "ID", "Parent1", "Parent2" and their cross prediction merit organized in descending order. -The results will also have sex information based on whether the dataset has plant sexes available in the database. - - diff --git a/docs/Gemfile b/docs/Gemfile deleted file mode 100644 index 187096c493..0000000000 --- a/docs/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -source 'https://rubygems.org' -gem 'github-pages', group: :jekyll_plugins - -gem "webrick", "~> 1.7" diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 9bf138d1cb..0000000000 --- a/docs/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# SGN Documentation -[**View the documentation**](http://solgenomics.github.io/sgn/) - -### Syntax and Use -The documentation is written in [kramdown-flavored](https://kramdown.gettalong.org/) markdown. It also takes advantage of the [Liquid](https://shopify.github.io/liquid/) templating system and the built-in variable in [Jekyll](https://jekyllrb.com), the static site generator that GitHub pages uses. - -This folder (`docs/`) can be used to build the site either via GitHUb pages or via a local Jekyll installation. - -### Guidelines for adding to the documentation - -#### Headers -```markdown -Header 1 OR -=========== - -# Header 1 - -Header 2 OR ------------ - -## Header 2 - -### Header 3 - -#### Header 4 - -##### Header 5 - -###### Header 6 -``` -You probably shouldnt use h1 headers, as the title of the page is always displayed as an h1, and using them elsewhere could be visualy confusing. - -**DONT USE BOLD INSTEAD OF HEADERS** (except in tables). Doing so makes generating a TOC impossible (and also visually looks off.) - -#### Horizontal Rules (Section Seperators) - -In kramdown, a horizontal rule must be preceeded and followed by a blank line: -```markdown -I am above the hr - ------------------ - -I am below the hr -``` - -#### Images/Screenshots - -_For screenshots_: try to make sure that the image is of an unbranded version of the db. - -To insert an image, we need to tell Jekyll to generate the relative path to the file like so -```markdown -![YOUR ALT TEXT]({{'assets/images/YOURIMAGE.png' | relative_url }}) -``` - -#### Links -To insert an link to something outside of the docs, we can use the usual markdown format: -```markdown -[LINK TEXT](http://your.link) -``` -If you want to link to a docs page, use this syntax. Note that we put the **path to the file** (from the `docs` directory), **not the rendered HTML page**, after `link`.): -```markdown -[LINK TEXT]({{ site.baseurl }}{% link your_folder/YOUR_FILE.md %}) -``` -If you want to link to a header on a docs page, we can extend the syntax above like so: -First, we assign the header we are linking to an ID: -```markdown -### I am the header {#your-header-id} -``` -then, we add that ID to the link: -```markdown -[LINK TEXT](#your-header-id) -[LINK TEXT]({{ site.baseurl }}{% link YOUR_FILE.md %}#your-header-id) -``` - -### Creating New Pages -**Every page should start with this YAML "front-matter" before anything else:** -```markdown ---- -title: "YOUR PAGE TITLE HERE" -layout: doc_page ---- -``` -**To insert a table of contents to a page with the following snippet (INCLUDING the comments [excluding them will cause the TOC to be indexed for search and not be readable by the TOC generator of folder pages.]):** -```markdown - -* TOC -{:toc} - -``` diff --git a/docs/_config.yml b/docs/_config.yml deleted file mode 100644 index 3d1d741537..0000000000 --- a/docs/_config.yml +++ /dev/null @@ -1,3 +0,0 @@ -theme: jekyll-theme-minimal -title: "SGN Documentation" -description:
diff --git a/docs/_includes/crumbs.html b/docs/_includes/crumbs.html deleted file mode 100644 index 59f682d373..0000000000 --- a/docs/_includes/crumbs.html +++ /dev/null @@ -1,18 +0,0 @@ -{% capture slug %}{{ page.url | split: "/" | last }}{% endcapture %} -{% capture parentURL %}{{ page.url | remove: slug | replace: "//", "/" }}{% endcapture %} -{% assign entries = site.pages | sort: "path" %} -{% if parentURL!=page.url %} - Home   - {% capture the_crumbs %} - - {% for pg in entries %} - {% if parentURL contains pg.url and pg.url!="/" %} - ←{{pg.title}}   - {% endif %} - {% endfor %} - - {% endcapture %} - {{the_crumbs | strip_newlines}} -{% else %} -
-{% endif %} diff --git a/docs/_includes/navigation.html b/docs/_includes/navigation.html deleted file mode 100644 index 0381fcfaa9..0000000000 --- a/docs/_includes/navigation.html +++ /dev/null @@ -1,31 +0,0 @@ - -{% capture html %} - - - {% endif %} - -{% endcapture %}{{ html | strip_newlines | replace:' ','' | replace:' ','' | replace:' ',' ' | remove: ''}} diff --git a/docs/_includes/search.html b/docs/_includes/search.html deleted file mode 100644 index 125a6cfc12..0000000000 --- a/docs/_includes/search.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - diff --git a/docs/_layouts/doc_folder.html b/docs/_layouts/doc_folder.html deleted file mode 100644 index 80c9e7b793..0000000000 --- a/docs/_layouts/doc_folder.html +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: doc_page ---- - -{% include navigation.html context=page.url %} - -{{content}} diff --git a/docs/_layouts/doc_page.html b/docs/_layouts/doc_page.html deleted file mode 100644 index 2d24f49052..0000000000 --- a/docs/_layouts/doc_page.html +++ /dev/null @@ -1,10 +0,0 @@ ---- -layout: default ---- -{% include crumbs.html context=page.url%} -
-{% include search.html %} - -

{{page.title}}

-{{content}} - diff --git a/docs/assets/css/style.scss b/docs/assets/css/style.scss deleted file mode 100644 index bb6c1e7241..0000000000 --- a/docs/assets/css/style.scss +++ /dev/null @@ -1,26 +0,0 @@ ---- ---- - -@import "{{ site.theme }}"; -@import "{{'assets/tipuesearch/css/tipuesearch.css' | relative_url }}"; - -ul#markdown-toc li, ul.nav-tree li{ - white-space: nowrap; -} - -ul#markdown-toc ul, ul.nav-tree ul{ - margin: 0.5em 0 0.5em 0; - padding-left: 1.25em; -} - -ul#markdown-toc li a{ - color: #1b5a87; -} - -ul.nav-tree ul ul{ - visibility: collapse !important; - height:0 !important; - overflow: none !important; - padding: 0 !important; - margin: 0 !important; -} diff --git a/docs/assets/gifs/gif1.gif b/docs/assets/gifs/gif1.gif deleted file mode 100644 index e2a4032837..0000000000 Binary files a/docs/assets/gifs/gif1.gif and /dev/null differ diff --git a/docs/assets/gifs/gif2.gif b/docs/assets/gifs/gif2.gif deleted file mode 100644 index 8a19226bc3..0000000000 Binary files a/docs/assets/gifs/gif2.gif and /dev/null differ diff --git a/docs/assets/gifs/gif3.gif b/docs/assets/gifs/gif3.gif deleted file mode 100644 index 2e57d17af3..0000000000 Binary files a/docs/assets/gifs/gif3.gif and /dev/null differ diff --git a/docs/assets/gifs/gif4.gif b/docs/assets/gifs/gif4.gif deleted file mode 100644 index 4391d95306..0000000000 Binary files a/docs/assets/gifs/gif4.gif and /dev/null differ diff --git a/docs/assets/images/Breedbase_HighRes.png b/docs/assets/images/Breedbase_HighRes.png deleted file mode 100644 index 327f8a40aa..0000000000 Binary files a/docs/assets/images/Breedbase_HighRes.png and /dev/null differ diff --git a/docs/assets/images/DatasetHelpImage001.png b/docs/assets/images/DatasetHelpImage001.png deleted file mode 100644 index 39fcadc1b3..0000000000 Binary files a/docs/assets/images/DatasetHelpImage001.png and /dev/null differ diff --git a/docs/assets/images/DatasetHelpImage002.png b/docs/assets/images/DatasetHelpImage002.png deleted file mode 100644 index a026bb1641..0000000000 Binary files a/docs/assets/images/DatasetHelpImage002.png and /dev/null differ diff --git a/docs/assets/images/DatasetHelpImage003.png b/docs/assets/images/DatasetHelpImage003.png deleted file mode 100644 index 90021ee69f..0000000000 Binary files a/docs/assets/images/DatasetHelpImage003.png and /dev/null differ diff --git a/docs/assets/images/DatasetHelpImage004.png b/docs/assets/images/DatasetHelpImage004.png deleted file mode 100644 index 190868b7ca..0000000000 Binary files a/docs/assets/images/DatasetHelpImage004.png and /dev/null differ diff --git a/docs/assets/images/DatasetHelpImage005.png b/docs/assets/images/DatasetHelpImage005.png deleted file mode 100644 index ede4564a33..0000000000 Binary files a/docs/assets/images/DatasetHelpImage005.png and /dev/null differ diff --git a/docs/assets/images/ZoomIn.png b/docs/assets/images/ZoomIn.png deleted file mode 100644 index 6f11f40ede..0000000000 Binary files a/docs/assets/images/ZoomIn.png and /dev/null differ diff --git a/docs/assets/images/ZoomOut.png b/docs/assets/images/ZoomOut.png deleted file mode 100644 index 1a213c28d4..0000000000 Binary files a/docs/assets/images/ZoomOut.png and /dev/null differ diff --git a/docs/assets/images/accession_upload_using_email.png b/docs/assets/images/accession_upload_using_email.png deleted file mode 100644 index beb797c95d..0000000000 Binary files a/docs/assets/images/accession_upload_using_email.png and /dev/null differ diff --git a/docs/assets/images/add_create_list.png b/docs/assets/images/add_create_list.png deleted file mode 100644 index ed90d674f3..0000000000 Binary files a/docs/assets/images/add_create_list.png and /dev/null differ diff --git a/docs/assets/images/add_cross.png b/docs/assets/images/add_cross.png deleted file mode 100644 index 8dffa72fe8..0000000000 Binary files a/docs/assets/images/add_cross.png and /dev/null differ diff --git a/docs/assets/images/add_cross_info.png b/docs/assets/images/add_cross_info.png deleted file mode 100644 index 31b4a0991d..0000000000 Binary files a/docs/assets/images/add_cross_info.png and /dev/null differ diff --git a/docs/assets/images/add_crossingtrial.png b/docs/assets/images/add_crossingtrial.png deleted file mode 100644 index 5b2253d305..0000000000 Binary files a/docs/assets/images/add_crossingtrial.png and /dev/null differ diff --git a/docs/assets/images/add_management_factor_dialog.png b/docs/assets/images/add_management_factor_dialog.png deleted file mode 100644 index 173817d2df..0000000000 Binary files a/docs/assets/images/add_management_factor_dialog.png and /dev/null differ diff --git a/docs/assets/images/add_management_factor_name_dialog.png b/docs/assets/images/add_management_factor_name_dialog.png deleted file mode 100644 index de31941a46..0000000000 Binary files a/docs/assets/images/add_management_factor_name_dialog.png and /dev/null differ diff --git a/docs/assets/images/add_transplanting_date.png b/docs/assets/images/add_transplanting_date.png deleted file mode 100644 index 6f352b79d8..0000000000 Binary files a/docs/assets/images/add_transplanting_date.png and /dev/null differ diff --git a/docs/assets/images/anova-dm.png b/docs/assets/images/anova-dm.png deleted file mode 100644 index 6fab8002f8..0000000000 Binary files a/docs/assets/images/anova-dm.png and /dev/null differ diff --git a/docs/assets/images/breeding_programs_screenshot.png b/docs/assets/images/breeding_programs_screenshot.png deleted file mode 100644 index 370b8e77a8..0000000000 Binary files a/docs/assets/images/breeding_programs_screenshot.png and /dev/null differ diff --git a/docs/assets/images/create_phenotype_spreadsheet_dialog.png b/docs/assets/images/create_phenotype_spreadsheet_dialog.png deleted file mode 100644 index 56cee006bf..0000000000 Binary files a/docs/assets/images/create_phenotype_spreadsheet_dialog.png and /dev/null differ diff --git a/docs/assets/images/cross_add_progenies.png b/docs/assets/images/cross_add_progenies.png deleted file mode 100644 index 5f2c6d7f17..0000000000 Binary files a/docs/assets/images/cross_add_progenies.png and /dev/null differ diff --git a/docs/assets/images/cross_page_1.png b/docs/assets/images/cross_page_1.png deleted file mode 100644 index 163cf9b8c2..0000000000 Binary files a/docs/assets/images/cross_page_1.png and /dev/null differ diff --git a/docs/assets/images/cross_page_2.png b/docs/assets/images/cross_page_2.png deleted file mode 100644 index 6322ce450a..0000000000 Binary files a/docs/assets/images/cross_page_2.png and /dev/null differ diff --git a/docs/assets/images/cross_upload_format.png b/docs/assets/images/cross_upload_format.png deleted file mode 100644 index d20f2de7a8..0000000000 Binary files a/docs/assets/images/cross_upload_format.png and /dev/null differ diff --git a/docs/assets/images/cross_upload_page.png b/docs/assets/images/cross_upload_page.png deleted file mode 100644 index cd2aec698a..0000000000 Binary files a/docs/assets/images/cross_upload_page.png and /dev/null differ diff --git a/docs/assets/images/cross_wishlist_initial_dialog.png b/docs/assets/images/cross_wishlist_initial_dialog.png deleted file mode 100644 index ee1a876a55..0000000000 Binary files a/docs/assets/images/cross_wishlist_initial_dialog.png and /dev/null differ diff --git a/docs/assets/images/cross_wishlist_not_using_list_01.png b/docs/assets/images/cross_wishlist_not_using_list_01.png deleted file mode 100644 index 9e17f10560..0000000000 Binary files a/docs/assets/images/cross_wishlist_not_using_list_01.png and /dev/null differ diff --git a/docs/assets/images/cross_wishlist_not_using_list_02.png b/docs/assets/images/cross_wishlist_not_using_list_02.png deleted file mode 100644 index d7113566a6..0000000000 Binary files a/docs/assets/images/cross_wishlist_not_using_list_02.png and /dev/null differ diff --git a/docs/assets/images/cross_wishlist_not_using_list_03.png b/docs/assets/images/cross_wishlist_not_using_list_03.png deleted file mode 100644 index 38f1448719..0000000000 Binary files a/docs/assets/images/cross_wishlist_not_using_list_03.png and /dev/null differ diff --git a/docs/assets/images/cross_wishlist_not_using_list_04.png b/docs/assets/images/cross_wishlist_not_using_list_04.png deleted file mode 100644 index 9078cf0224..0000000000 Binary files a/docs/assets/images/cross_wishlist_not_using_list_04.png and /dev/null differ diff --git a/docs/assets/images/cross_wishlist_using_list_01.png b/docs/assets/images/cross_wishlist_using_list_01.png deleted file mode 100644 index 36c8ee2a0a..0000000000 Binary files a/docs/assets/images/cross_wishlist_using_list_01.png and /dev/null differ diff --git a/docs/assets/images/cross_wishlist_using_list_02.png b/docs/assets/images/cross_wishlist_using_list_02.png deleted file mode 100644 index 4c656f9c93..0000000000 Binary files a/docs/assets/images/cross_wishlist_using_list_02.png and /dev/null differ diff --git a/docs/assets/images/crossinfo_upload_spreadsheet.png b/docs/assets/images/crossinfo_upload_spreadsheet.png deleted file mode 100644 index 05c4d73197..0000000000 Binary files a/docs/assets/images/crossinfo_upload_spreadsheet.png and /dev/null differ diff --git a/docs/assets/images/crossingtrial_1.png b/docs/assets/images/crossingtrial_1.png deleted file mode 100644 index d0891ffd66..0000000000 Binary files a/docs/assets/images/crossingtrial_1.png and /dev/null differ diff --git a/docs/assets/images/crossingtrial_2.png b/docs/assets/images/crossingtrial_2.png deleted file mode 100644 index 60bc304c48..0000000000 Binary files a/docs/assets/images/crossingtrial_2.png and /dev/null differ diff --git a/docs/assets/images/cursorLocation.png b/docs/assets/images/cursorLocation.png deleted file mode 100644 index 1ddde75b7c..0000000000 Binary files a/docs/assets/images/cursorLocation.png and /dev/null differ diff --git a/docs/assets/images/edit_cross_info.png b/docs/assets/images/edit_cross_info.png deleted file mode 100644 index 299012994c..0000000000 Binary files a/docs/assets/images/edit_cross_info.png and /dev/null differ diff --git a/docs/assets/images/field_map_download_dialog.png b/docs/assets/images/field_map_download_dialog.png deleted file mode 100644 index d2db3219a5..0000000000 Binary files a/docs/assets/images/field_map_download_dialog.png and /dev/null differ diff --git a/docs/assets/images/field_map_download_link.png b/docs/assets/images/field_map_download_link.png deleted file mode 100644 index 74b4d1f84b..0000000000 Binary files a/docs/assets/images/field_map_download_link.png and /dev/null differ diff --git a/docs/assets/images/field_map_download_spreadsheet.png b/docs/assets/images/field_map_download_spreadsheet.png deleted file mode 100644 index 301a967bf0..0000000000 Binary files a/docs/assets/images/field_map_download_spreadsheet.png and /dev/null differ diff --git a/docs/assets/images/fieldmap_display_plot_image.png b/docs/assets/images/fieldmap_display_plot_image.png deleted file mode 100644 index ae0e62050c..0000000000 Binary files a/docs/assets/images/fieldmap_display_plot_image.png and /dev/null differ diff --git a/docs/assets/images/fieldmap_multi_trial_layout.png b/docs/assets/images/fieldmap_multi_trial_layout.png deleted file mode 100644 index e25d6a62ac..0000000000 Binary files a/docs/assets/images/fieldmap_multi_trial_layout.png and /dev/null differ diff --git a/docs/assets/images/fieldmap_plot_image.png b/docs/assets/images/fieldmap_plot_image.png deleted file mode 100644 index 810618f60a..0000000000 Binary files a/docs/assets/images/fieldmap_plot_image.png and /dev/null differ diff --git a/docs/assets/images/fieldmap_trial_layout.png b/docs/assets/images/fieldmap_trial_layout.png deleted file mode 100644 index 7e9ee21cfd..0000000000 Binary files a/docs/assets/images/fieldmap_trial_layout.png and /dev/null differ diff --git a/docs/assets/images/fieldmap_view_plot_image.png b/docs/assets/images/fieldmap_view_plot_image.png deleted file mode 100644 index 97eebb3ea3..0000000000 Binary files a/docs/assets/images/fieldmap_view_plot_image.png and /dev/null differ diff --git a/docs/assets/images/filename_verify_success.png b/docs/assets/images/filename_verify_success.png deleted file mode 100644 index 001eff461c..0000000000 Binary files a/docs/assets/images/filename_verify_success.png and /dev/null differ diff --git a/docs/assets/images/genetic-gain.png b/docs/assets/images/genetic-gain.png deleted file mode 100644 index b03ac03425..0000000000 Binary files a/docs/assets/images/genetic-gain.png and /dev/null differ diff --git a/docs/assets/images/genotyping_protocol_detail_page_delete.png b/docs/assets/images/genotyping_protocol_detail_page_delete.png deleted file mode 100644 index 95a7badd4c..0000000000 Binary files a/docs/assets/images/genotyping_protocol_detail_page_delete.png and /dev/null differ diff --git a/docs/assets/images/genotyping_protocol_detail_page_markers.png b/docs/assets/images/genotyping_protocol_detail_page_markers.png deleted file mode 100644 index eaa847ffbd..0000000000 Binary files a/docs/assets/images/genotyping_protocol_detail_page_markers.png and /dev/null differ diff --git a/docs/assets/images/genotyping_protocol_detail_page_samples.png b/docs/assets/images/genotyping_protocol_detail_page_samples.png deleted file mode 100644 index 2610bd38dc..0000000000 Binary files a/docs/assets/images/genotyping_protocol_detail_page_samples.png and /dev/null differ diff --git a/docs/assets/images/genotyping_protocols_search.png b/docs/assets/images/genotyping_protocols_search.png deleted file mode 100644 index 306ee9853b..0000000000 Binary files a/docs/assets/images/genotyping_protocols_search.png and /dev/null differ diff --git a/docs/assets/images/hclustering.png b/docs/assets/images/hclustering.png deleted file mode 100644 index 283b1653be..0000000000 Binary files a/docs/assets/images/hclustering.png and /dev/null differ diff --git a/docs/assets/images/heatmap_assayed_trait_view.png b/docs/assets/images/heatmap_assayed_trait_view.png deleted file mode 100644 index 97b2ee8865..0000000000 Binary files a/docs/assets/images/heatmap_assayed_trait_view.png and /dev/null differ diff --git a/docs/assets/images/heatmap_default_display.png b/docs/assets/images/heatmap_default_display.png deleted file mode 100644 index cc2ce7daa1..0000000000 Binary files a/docs/assets/images/heatmap_default_display.png and /dev/null differ diff --git a/docs/assets/images/heatmap_spatial_correction_button.png b/docs/assets/images/heatmap_spatial_correction_button.png deleted file mode 100644 index adc85fd6aa..0000000000 Binary files a/docs/assets/images/heatmap_spatial_correction_button.png and /dev/null differ diff --git a/docs/assets/images/image112.png b/docs/assets/images/image112.png deleted file mode 100755 index 0b80957ce9..0000000000 Binary files a/docs/assets/images/image112.png and /dev/null differ diff --git a/docs/assets/images/image114.png b/docs/assets/images/image114.png deleted file mode 100755 index 746181e110..0000000000 Binary files a/docs/assets/images/image114.png and /dev/null differ diff --git a/docs/assets/images/image115.png b/docs/assets/images/image115.png deleted file mode 100755 index 419b5e10ae..0000000000 Binary files a/docs/assets/images/image115.png and /dev/null differ diff --git a/docs/assets/images/image116.png b/docs/assets/images/image116.png deleted file mode 100755 index fce3a0c594..0000000000 Binary files a/docs/assets/images/image116.png and /dev/null differ diff --git a/docs/assets/images/image12.png b/docs/assets/images/image12.png deleted file mode 100755 index 1bac7db039..0000000000 Binary files a/docs/assets/images/image12.png and /dev/null differ diff --git a/docs/assets/images/image131.png b/docs/assets/images/image131.png deleted file mode 100755 index e6e53d9352..0000000000 Binary files a/docs/assets/images/image131.png and /dev/null differ diff --git a/docs/assets/images/image133.png b/docs/assets/images/image133.png deleted file mode 100755 index 82d88ccc5f..0000000000 Binary files a/docs/assets/images/image133.png and /dev/null differ diff --git a/docs/assets/images/image134.png b/docs/assets/images/image134.png deleted file mode 100755 index 95c7a62740..0000000000 Binary files a/docs/assets/images/image134.png and /dev/null differ diff --git a/docs/assets/images/image135.png b/docs/assets/images/image135.png deleted file mode 100755 index 1a9bc7698c..0000000000 Binary files a/docs/assets/images/image135.png and /dev/null differ diff --git a/docs/assets/images/image136.png b/docs/assets/images/image136.png deleted file mode 100755 index cecfe15ed7..0000000000 Binary files a/docs/assets/images/image136.png and /dev/null differ diff --git a/docs/assets/images/image138.png b/docs/assets/images/image138.png deleted file mode 100755 index 6ef8fe51c0..0000000000 Binary files a/docs/assets/images/image138.png and /dev/null differ diff --git a/docs/assets/images/image140.png b/docs/assets/images/image140.png deleted file mode 100755 index 4498435d00..0000000000 Binary files a/docs/assets/images/image140.png and /dev/null differ diff --git a/docs/assets/images/image141.png b/docs/assets/images/image141.png deleted file mode 100755 index 705f69139b..0000000000 Binary files a/docs/assets/images/image141.png and /dev/null differ diff --git a/docs/assets/images/image142.png b/docs/assets/images/image142.png deleted file mode 100755 index ba8bb65557..0000000000 Binary files a/docs/assets/images/image142.png and /dev/null differ diff --git a/docs/assets/images/image144.png b/docs/assets/images/image144.png deleted file mode 100755 index f61d399c2c..0000000000 Binary files a/docs/assets/images/image144.png and /dev/null differ diff --git a/docs/assets/images/image146.png b/docs/assets/images/image146.png deleted file mode 100755 index 7cc666215d..0000000000 Binary files a/docs/assets/images/image146.png and /dev/null differ diff --git a/docs/assets/images/image147.png b/docs/assets/images/image147.png deleted file mode 100755 index e9dcc6562e..0000000000 Binary files a/docs/assets/images/image147.png and /dev/null differ diff --git a/docs/assets/images/image148.png b/docs/assets/images/image148.png deleted file mode 100755 index de1cf05066..0000000000 Binary files a/docs/assets/images/image148.png and /dev/null differ diff --git a/docs/assets/images/image149.png b/docs/assets/images/image149.png deleted file mode 100755 index 2266b03e09..0000000000 Binary files a/docs/assets/images/image149.png and /dev/null differ diff --git a/docs/assets/images/image150.png b/docs/assets/images/image150.png deleted file mode 100755 index 7bbd333b97..0000000000 Binary files a/docs/assets/images/image150.png and /dev/null differ diff --git a/docs/assets/images/image151.png b/docs/assets/images/image151.png deleted file mode 100755 index 4fb1e7ce89..0000000000 Binary files a/docs/assets/images/image151.png and /dev/null differ diff --git a/docs/assets/images/image152.png b/docs/assets/images/image152.png deleted file mode 100755 index 17c9320c04..0000000000 Binary files a/docs/assets/images/image152.png and /dev/null differ diff --git a/docs/assets/images/image153.png b/docs/assets/images/image153.png deleted file mode 100755 index 6d73dc3465..0000000000 Binary files a/docs/assets/images/image153.png and /dev/null differ diff --git a/docs/assets/images/image154.png b/docs/assets/images/image154.png deleted file mode 100755 index ea4c6bf41c..0000000000 Binary files a/docs/assets/images/image154.png and /dev/null differ diff --git a/docs/assets/images/image155.png b/docs/assets/images/image155.png deleted file mode 100755 index f286c61bec..0000000000 Binary files a/docs/assets/images/image155.png and /dev/null differ diff --git a/docs/assets/images/image156.png b/docs/assets/images/image156.png deleted file mode 100755 index 1c40ee4de8..0000000000 Binary files a/docs/assets/images/image156.png and /dev/null differ diff --git a/docs/assets/images/image157.png b/docs/assets/images/image157.png deleted file mode 100755 index 46add37382..0000000000 Binary files a/docs/assets/images/image157.png and /dev/null differ diff --git a/docs/assets/images/image158.png b/docs/assets/images/image158.png deleted file mode 100755 index f4d62347c0..0000000000 Binary files a/docs/assets/images/image158.png and /dev/null differ diff --git a/docs/assets/images/image16.png b/docs/assets/images/image16.png deleted file mode 100755 index dd9562b258..0000000000 Binary files a/docs/assets/images/image16.png and /dev/null differ diff --git a/docs/assets/images/image160.png b/docs/assets/images/image160.png deleted file mode 100755 index 4cbd495334..0000000000 Binary files a/docs/assets/images/image160.png and /dev/null differ diff --git a/docs/assets/images/image161.png b/docs/assets/images/image161.png deleted file mode 100644 index 1c1bc1c5f8..0000000000 Binary files a/docs/assets/images/image161.png and /dev/null differ diff --git a/docs/assets/images/image162.png b/docs/assets/images/image162.png deleted file mode 100755 index 5a0ba99f6e..0000000000 Binary files a/docs/assets/images/image162.png and /dev/null differ diff --git a/docs/assets/images/image163.png b/docs/assets/images/image163.png deleted file mode 100755 index 83fb9cfe59..0000000000 Binary files a/docs/assets/images/image163.png and /dev/null differ diff --git a/docs/assets/images/image165.png b/docs/assets/images/image165.png deleted file mode 100755 index 373c6ff5bb..0000000000 Binary files a/docs/assets/images/image165.png and /dev/null differ diff --git a/docs/assets/images/image166.png b/docs/assets/images/image166.png deleted file mode 100755 index f71291056c..0000000000 Binary files a/docs/assets/images/image166.png and /dev/null differ diff --git a/docs/assets/images/image170.png b/docs/assets/images/image170.png deleted file mode 100755 index 82d88ccc5f..0000000000 Binary files a/docs/assets/images/image170.png and /dev/null differ diff --git a/docs/assets/images/image171.png b/docs/assets/images/image171.png deleted file mode 100755 index 7a54c60577..0000000000 Binary files a/docs/assets/images/image171.png and /dev/null differ diff --git a/docs/assets/images/image173.png b/docs/assets/images/image173.png deleted file mode 100755 index 6ae4bbeba3..0000000000 Binary files a/docs/assets/images/image173.png and /dev/null differ diff --git a/docs/assets/images/image174.png b/docs/assets/images/image174.png deleted file mode 100755 index 94140f5086..0000000000 Binary files a/docs/assets/images/image174.png and /dev/null differ diff --git a/docs/assets/images/image175.png b/docs/assets/images/image175.png deleted file mode 100755 index 998fa01279..0000000000 Binary files a/docs/assets/images/image175.png and /dev/null differ diff --git a/docs/assets/images/image177.png b/docs/assets/images/image177.png deleted file mode 100755 index d18b19555a..0000000000 Binary files a/docs/assets/images/image177.png and /dev/null differ diff --git a/docs/assets/images/image178.png b/docs/assets/images/image178.png deleted file mode 100755 index 1642648821..0000000000 Binary files a/docs/assets/images/image178.png and /dev/null differ diff --git a/docs/assets/images/image181.png b/docs/assets/images/image181.png deleted file mode 100755 index b8f0dbfecb..0000000000 Binary files a/docs/assets/images/image181.png and /dev/null differ diff --git a/docs/assets/images/image182.png b/docs/assets/images/image182.png deleted file mode 100755 index d1b7c82b8a..0000000000 Binary files a/docs/assets/images/image182.png and /dev/null differ diff --git a/docs/assets/images/image183.png b/docs/assets/images/image183.png deleted file mode 100644 index 43cd8786ea..0000000000 Binary files a/docs/assets/images/image183.png and /dev/null differ diff --git a/docs/assets/images/image184.png b/docs/assets/images/image184.png deleted file mode 100755 index 12a7d7d721..0000000000 Binary files a/docs/assets/images/image184.png and /dev/null differ diff --git a/docs/assets/images/image185.png b/docs/assets/images/image185.png deleted file mode 100755 index 97f780f01c..0000000000 Binary files a/docs/assets/images/image185.png and /dev/null differ diff --git a/docs/assets/images/image187.png b/docs/assets/images/image187.png deleted file mode 100755 index 3f3a75cb86..0000000000 Binary files a/docs/assets/images/image187.png and /dev/null differ diff --git a/docs/assets/images/image189.png b/docs/assets/images/image189.png deleted file mode 100755 index 12991b499c..0000000000 Binary files a/docs/assets/images/image189.png and /dev/null differ diff --git a/docs/assets/images/image190.png b/docs/assets/images/image190.png deleted file mode 100755 index 5291fa3849..0000000000 Binary files a/docs/assets/images/image190.png and /dev/null differ diff --git a/docs/assets/images/image20.png b/docs/assets/images/image20.png deleted file mode 100755 index 499143eccc..0000000000 Binary files a/docs/assets/images/image20.png and /dev/null differ diff --git a/docs/assets/images/image21.png b/docs/assets/images/image21.png deleted file mode 100755 index 914ce3e30e..0000000000 Binary files a/docs/assets/images/image21.png and /dev/null differ diff --git a/docs/assets/images/image22.png b/docs/assets/images/image22.png deleted file mode 100755 index 8819e07eda..0000000000 Binary files a/docs/assets/images/image22.png and /dev/null differ diff --git a/docs/assets/images/image243.png b/docs/assets/images/image243.png deleted file mode 100755 index 6d66494469..0000000000 Binary files a/docs/assets/images/image243.png and /dev/null differ diff --git a/docs/assets/images/image244.png b/docs/assets/images/image244.png deleted file mode 100755 index 7f9dabe127..0000000000 Binary files a/docs/assets/images/image244.png and /dev/null differ diff --git a/docs/assets/images/image245.png b/docs/assets/images/image245.png deleted file mode 100755 index c06cc28436..0000000000 Binary files a/docs/assets/images/image245.png and /dev/null differ diff --git a/docs/assets/images/image246.png b/docs/assets/images/image246.png deleted file mode 100755 index 2bd5719702..0000000000 Binary files a/docs/assets/images/image246.png and /dev/null differ diff --git a/docs/assets/images/image247.png b/docs/assets/images/image247.png deleted file mode 100755 index 3c0e86d875..0000000000 Binary files a/docs/assets/images/image247.png and /dev/null differ diff --git a/docs/assets/images/image248.png b/docs/assets/images/image248.png deleted file mode 100755 index 1eb3efa6d5..0000000000 Binary files a/docs/assets/images/image248.png and /dev/null differ diff --git a/docs/assets/images/image249.png b/docs/assets/images/image249.png deleted file mode 100755 index 1f31b1eca8..0000000000 Binary files a/docs/assets/images/image249.png and /dev/null differ diff --git a/docs/assets/images/image251.png b/docs/assets/images/image251.png deleted file mode 100755 index ce3614e8ca..0000000000 Binary files a/docs/assets/images/image251.png and /dev/null differ diff --git a/docs/assets/images/image252.png b/docs/assets/images/image252.png deleted file mode 100755 index 35e2d351b2..0000000000 Binary files a/docs/assets/images/image252.png and /dev/null differ diff --git a/docs/assets/images/image253.png b/docs/assets/images/image253.png deleted file mode 100755 index 5abd21b6c0..0000000000 Binary files a/docs/assets/images/image253.png and /dev/null differ diff --git a/docs/assets/images/image254.png b/docs/assets/images/image254.png deleted file mode 100755 index a0afa9c542..0000000000 Binary files a/docs/assets/images/image254.png and /dev/null differ diff --git a/docs/assets/images/image255.png b/docs/assets/images/image255.png deleted file mode 100755 index af493d613f..0000000000 Binary files a/docs/assets/images/image255.png and /dev/null differ diff --git a/docs/assets/images/image256.png b/docs/assets/images/image256.png deleted file mode 100755 index 47cd197157..0000000000 Binary files a/docs/assets/images/image256.png and /dev/null differ diff --git a/docs/assets/images/image257.png b/docs/assets/images/image257.png deleted file mode 100755 index a90e411b41..0000000000 Binary files a/docs/assets/images/image257.png and /dev/null differ diff --git a/docs/assets/images/image258.png b/docs/assets/images/image258.png deleted file mode 100755 index ea6e94218f..0000000000 Binary files a/docs/assets/images/image258.png and /dev/null differ diff --git a/docs/assets/images/image261.png b/docs/assets/images/image261.png deleted file mode 100755 index e409ac7a54..0000000000 Binary files a/docs/assets/images/image261.png and /dev/null differ diff --git a/docs/assets/images/image262.png b/docs/assets/images/image262.png deleted file mode 100755 index aa8e40544b..0000000000 Binary files a/docs/assets/images/image262.png and /dev/null differ diff --git a/docs/assets/images/image263.png b/docs/assets/images/image263.png deleted file mode 100755 index fa255d7210..0000000000 Binary files a/docs/assets/images/image263.png and /dev/null differ diff --git a/docs/assets/images/image264.png b/docs/assets/images/image264.png deleted file mode 100755 index 7f92ca86fe..0000000000 Binary files a/docs/assets/images/image264.png and /dev/null differ diff --git a/docs/assets/images/image265.png b/docs/assets/images/image265.png deleted file mode 100755 index f46b69fe61..0000000000 Binary files a/docs/assets/images/image265.png and /dev/null differ diff --git a/docs/assets/images/image267.png b/docs/assets/images/image267.png deleted file mode 100755 index bedc89d416..0000000000 Binary files a/docs/assets/images/image267.png and /dev/null differ diff --git a/docs/assets/images/image268.png b/docs/assets/images/image268.png deleted file mode 100755 index c3663cc645..0000000000 Binary files a/docs/assets/images/image268.png and /dev/null differ diff --git a/docs/assets/images/image269.png b/docs/assets/images/image269.png deleted file mode 100755 index 13b7a4dc7e..0000000000 Binary files a/docs/assets/images/image269.png and /dev/null differ diff --git a/docs/assets/images/image270.png b/docs/assets/images/image270.png deleted file mode 100755 index 3561968d63..0000000000 Binary files a/docs/assets/images/image270.png and /dev/null differ diff --git a/docs/assets/images/image273.png b/docs/assets/images/image273.png deleted file mode 100755 index 53d094c342..0000000000 Binary files a/docs/assets/images/image273.png and /dev/null differ diff --git a/docs/assets/images/image274.png b/docs/assets/images/image274.png deleted file mode 100755 index 0a5e431368..0000000000 Binary files a/docs/assets/images/image274.png and /dev/null differ diff --git a/docs/assets/images/image276.png b/docs/assets/images/image276.png deleted file mode 100755 index 0180709304..0000000000 Binary files a/docs/assets/images/image276.png and /dev/null differ diff --git a/docs/assets/images/image277.png b/docs/assets/images/image277.png deleted file mode 100755 index e9b92546dc..0000000000 Binary files a/docs/assets/images/image277.png and /dev/null differ diff --git a/docs/assets/images/image279.png b/docs/assets/images/image279.png deleted file mode 100755 index f7142bda01..0000000000 Binary files a/docs/assets/images/image279.png and /dev/null differ diff --git a/docs/assets/images/image281.png b/docs/assets/images/image281.png deleted file mode 100755 index ee7d2bfd03..0000000000 Binary files a/docs/assets/images/image281.png and /dev/null differ diff --git a/docs/assets/images/image282.png b/docs/assets/images/image282.png deleted file mode 100755 index f04a0b19ed..0000000000 Binary files a/docs/assets/images/image282.png and /dev/null differ diff --git a/docs/assets/images/image285.png b/docs/assets/images/image285.png deleted file mode 100755 index 762cb603be..0000000000 Binary files a/docs/assets/images/image285.png and /dev/null differ diff --git a/docs/assets/images/image286.png b/docs/assets/images/image286.png deleted file mode 100755 index 9508584286..0000000000 Binary files a/docs/assets/images/image286.png and /dev/null differ diff --git a/docs/assets/images/image287.png b/docs/assets/images/image287.png deleted file mode 100755 index ec380e7aca..0000000000 Binary files a/docs/assets/images/image287.png and /dev/null differ diff --git a/docs/assets/images/image288.png b/docs/assets/images/image288.png deleted file mode 100755 index f88361de94..0000000000 Binary files a/docs/assets/images/image288.png and /dev/null differ diff --git a/docs/assets/images/image290.png b/docs/assets/images/image290.png deleted file mode 100755 index e32886df83..0000000000 Binary files a/docs/assets/images/image290.png and /dev/null differ diff --git a/docs/assets/images/image295.png b/docs/assets/images/image295.png deleted file mode 100755 index da3649ee0e..0000000000 Binary files a/docs/assets/images/image295.png and /dev/null differ diff --git a/docs/assets/images/image296.png b/docs/assets/images/image296.png deleted file mode 100755 index 954ef7ce0f..0000000000 Binary files a/docs/assets/images/image296.png and /dev/null differ diff --git a/docs/assets/images/image297.png b/docs/assets/images/image297.png deleted file mode 100755 index 2c52987ac6..0000000000 Binary files a/docs/assets/images/image297.png and /dev/null differ diff --git a/docs/assets/images/image298.png b/docs/assets/images/image298.png deleted file mode 100755 index 5b6c907add..0000000000 Binary files a/docs/assets/images/image298.png and /dev/null differ diff --git a/docs/assets/images/image302.png b/docs/assets/images/image302.png deleted file mode 100755 index 82d88ccc5f..0000000000 Binary files a/docs/assets/images/image302.png and /dev/null differ diff --git a/docs/assets/images/image303.png b/docs/assets/images/image303.png deleted file mode 100755 index aa9a47d304..0000000000 Binary files a/docs/assets/images/image303.png and /dev/null differ diff --git a/docs/assets/images/image304.png b/docs/assets/images/image304.png deleted file mode 100755 index 20b6a7f87a..0000000000 Binary files a/docs/assets/images/image304.png and /dev/null differ diff --git a/docs/assets/images/image314.png b/docs/assets/images/image314.png deleted file mode 100755 index 1795799bca..0000000000 Binary files a/docs/assets/images/image314.png and /dev/null differ diff --git a/docs/assets/images/image317.png b/docs/assets/images/image317.png deleted file mode 100755 index 24f34d1212..0000000000 Binary files a/docs/assets/images/image317.png and /dev/null differ diff --git a/docs/assets/images/image318.png b/docs/assets/images/image318.png deleted file mode 100755 index 7994cc76c4..0000000000 Binary files a/docs/assets/images/image318.png and /dev/null differ diff --git a/docs/assets/images/image319.png b/docs/assets/images/image319.png deleted file mode 100755 index 7978553330..0000000000 Binary files a/docs/assets/images/image319.png and /dev/null differ diff --git a/docs/assets/images/image320.png b/docs/assets/images/image320.png deleted file mode 100755 index 328483ccf1..0000000000 Binary files a/docs/assets/images/image320.png and /dev/null differ diff --git a/docs/assets/images/image321.png b/docs/assets/images/image321.png deleted file mode 100755 index 225349d2d1..0000000000 Binary files a/docs/assets/images/image321.png and /dev/null differ diff --git a/docs/assets/images/image322.png b/docs/assets/images/image322.png deleted file mode 100755 index c0810dd9af..0000000000 Binary files a/docs/assets/images/image322.png and /dev/null differ diff --git a/docs/assets/images/image324.png b/docs/assets/images/image324.png deleted file mode 100755 index 8d0a69b00c..0000000000 Binary files a/docs/assets/images/image324.png and /dev/null differ diff --git a/docs/assets/images/image325.png b/docs/assets/images/image325.png deleted file mode 100755 index 4e5fef3726..0000000000 Binary files a/docs/assets/images/image325.png and /dev/null differ diff --git a/docs/assets/images/image326.png b/docs/assets/images/image326.png deleted file mode 100755 index c74de20ea8..0000000000 Binary files a/docs/assets/images/image326.png and /dev/null differ diff --git a/docs/assets/images/image327.png b/docs/assets/images/image327.png deleted file mode 100755 index b00a978397..0000000000 Binary files a/docs/assets/images/image327.png and /dev/null differ diff --git a/docs/assets/images/image328.png b/docs/assets/images/image328.png deleted file mode 100755 index 8f4cb6e69f..0000000000 Binary files a/docs/assets/images/image328.png and /dev/null differ diff --git a/docs/assets/images/image329.png b/docs/assets/images/image329.png deleted file mode 100755 index 69053efe9b..0000000000 Binary files a/docs/assets/images/image329.png and /dev/null differ diff --git a/docs/assets/images/image330.png b/docs/assets/images/image330.png deleted file mode 100755 index 95ac06fcf5..0000000000 Binary files a/docs/assets/images/image330.png and /dev/null differ diff --git a/docs/assets/images/image331.png b/docs/assets/images/image331.png deleted file mode 100755 index dca1dd4e9d..0000000000 Binary files a/docs/assets/images/image331.png and /dev/null differ diff --git a/docs/assets/images/image332.png b/docs/assets/images/image332.png deleted file mode 100644 index 4d823cc22d..0000000000 Binary files a/docs/assets/images/image332.png and /dev/null differ diff --git a/docs/assets/images/image333.png b/docs/assets/images/image333.png deleted file mode 100755 index 95498cefdb..0000000000 Binary files a/docs/assets/images/image333.png and /dev/null differ diff --git a/docs/assets/images/image334.png b/docs/assets/images/image334.png deleted file mode 100755 index 3c5e6a4250..0000000000 Binary files a/docs/assets/images/image334.png and /dev/null differ diff --git a/docs/assets/images/image336.png b/docs/assets/images/image336.png deleted file mode 100755 index d44abab00b..0000000000 Binary files a/docs/assets/images/image336.png and /dev/null differ diff --git a/docs/assets/images/image337.png b/docs/assets/images/image337.png deleted file mode 100644 index 5019100cee..0000000000 Binary files a/docs/assets/images/image337.png and /dev/null differ diff --git a/docs/assets/images/image338.png b/docs/assets/images/image338.png deleted file mode 100755 index 234ce440a8..0000000000 Binary files a/docs/assets/images/image338.png and /dev/null differ diff --git a/docs/assets/images/image340.png b/docs/assets/images/image340.png deleted file mode 100755 index f187b051a2..0000000000 Binary files a/docs/assets/images/image340.png and /dev/null differ diff --git a/docs/assets/images/image341.png b/docs/assets/images/image341.png deleted file mode 100755 index 264cb8cc7c..0000000000 Binary files a/docs/assets/images/image341.png and /dev/null differ diff --git a/docs/assets/images/image342.png b/docs/assets/images/image342.png deleted file mode 100755 index 4511681336..0000000000 Binary files a/docs/assets/images/image342.png and /dev/null differ diff --git a/docs/assets/images/image343.png b/docs/assets/images/image343.png deleted file mode 100755 index 30dba1abbb..0000000000 Binary files a/docs/assets/images/image343.png and /dev/null differ diff --git a/docs/assets/images/image344.png b/docs/assets/images/image344.png deleted file mode 100755 index a4f3b0989e..0000000000 Binary files a/docs/assets/images/image344.png and /dev/null differ diff --git a/docs/assets/images/image345.png b/docs/assets/images/image345.png deleted file mode 100755 index e3c149a6c3..0000000000 Binary files a/docs/assets/images/image345.png and /dev/null differ diff --git a/docs/assets/images/image346.png b/docs/assets/images/image346.png deleted file mode 100755 index c4a917a29f..0000000000 Binary files a/docs/assets/images/image346.png and /dev/null differ diff --git a/docs/assets/images/image347.png b/docs/assets/images/image347.png deleted file mode 100644 index 6758585949..0000000000 Binary files a/docs/assets/images/image347.png and /dev/null differ diff --git a/docs/assets/images/image348.png b/docs/assets/images/image348.png deleted file mode 100644 index cc815e4e0e..0000000000 Binary files a/docs/assets/images/image348.png and /dev/null differ diff --git a/docs/assets/images/image68.png b/docs/assets/images/image68.png deleted file mode 100755 index 7073a452d6..0000000000 Binary files a/docs/assets/images/image68.png and /dev/null differ diff --git a/docs/assets/images/image69.png b/docs/assets/images/image69.png deleted file mode 100755 index c0810dd9af..0000000000 Binary files a/docs/assets/images/image69.png and /dev/null differ diff --git a/docs/assets/images/image71.png b/docs/assets/images/image71.png deleted file mode 100755 index ae11221bb4..0000000000 Binary files a/docs/assets/images/image71.png and /dev/null differ diff --git a/docs/assets/images/image72.png b/docs/assets/images/image72.png deleted file mode 100755 index b49869af5c..0000000000 Binary files a/docs/assets/images/image72.png and /dev/null differ diff --git a/docs/assets/images/image74.png b/docs/assets/images/image74.png deleted file mode 100755 index 46e6017f0e..0000000000 Binary files a/docs/assets/images/image74.png and /dev/null differ diff --git a/docs/assets/images/image75.png b/docs/assets/images/image75.png deleted file mode 100755 index c844460a0c..0000000000 Binary files a/docs/assets/images/image75.png and /dev/null differ diff --git a/docs/assets/images/image76.png b/docs/assets/images/image76.png deleted file mode 100755 index d938c39e5d..0000000000 Binary files a/docs/assets/images/image76.png and /dev/null differ diff --git a/docs/assets/images/image78.png b/docs/assets/images/image78.png deleted file mode 100755 index a4dc9e6860..0000000000 Binary files a/docs/assets/images/image78.png and /dev/null differ diff --git a/docs/assets/images/image79.png b/docs/assets/images/image79.png deleted file mode 100755 index 8ff809847d..0000000000 Binary files a/docs/assets/images/image79.png and /dev/null differ diff --git a/docs/assets/images/image81.png b/docs/assets/images/image81.png deleted file mode 100755 index 8b220cb666..0000000000 Binary files a/docs/assets/images/image81.png and /dev/null differ diff --git a/docs/assets/images/image82.png b/docs/assets/images/image82.png deleted file mode 100755 index f65c59fa97..0000000000 Binary files a/docs/assets/images/image82.png and /dev/null differ diff --git a/docs/assets/images/image83.png b/docs/assets/images/image83.png deleted file mode 100755 index fb54375a00..0000000000 Binary files a/docs/assets/images/image83.png and /dev/null differ diff --git a/docs/assets/images/image85.png b/docs/assets/images/image85.png deleted file mode 100755 index fcb0f9f4ad..0000000000 Binary files a/docs/assets/images/image85.png and /dev/null differ diff --git a/docs/assets/images/image87.png b/docs/assets/images/image87.png deleted file mode 100755 index 36b4206c93..0000000000 Binary files a/docs/assets/images/image87.png and /dev/null differ diff --git a/docs/assets/images/image88.png b/docs/assets/images/image88.png deleted file mode 100755 index fd282e41d8..0000000000 Binary files a/docs/assets/images/image88.png and /dev/null differ diff --git a/docs/assets/images/image89.png b/docs/assets/images/image89.png deleted file mode 100755 index 067433f499..0000000000 Binary files a/docs/assets/images/image89.png and /dev/null differ diff --git a/docs/assets/images/image90.png b/docs/assets/images/image90.png deleted file mode 100755 index 5b03b66b6a..0000000000 Binary files a/docs/assets/images/image90.png and /dev/null differ diff --git a/docs/assets/images/image92.png b/docs/assets/images/image92.png deleted file mode 100755 index e7109d347f..0000000000 Binary files a/docs/assets/images/image92.png and /dev/null differ diff --git a/docs/assets/images/image93.png b/docs/assets/images/image93.png deleted file mode 100755 index 076e7f3a61..0000000000 Binary files a/docs/assets/images/image93.png and /dev/null differ diff --git a/docs/assets/images/image94.png b/docs/assets/images/image94.png deleted file mode 100755 index a90e411b41..0000000000 Binary files a/docs/assets/images/image94.png and /dev/null differ diff --git a/docs/assets/images/image95.png b/docs/assets/images/image95.png deleted file mode 100755 index aeaaeb7d85..0000000000 Binary files a/docs/assets/images/image95.png and /dev/null differ diff --git a/docs/assets/images/image_upload_dialog.png b/docs/assets/images/image_upload_dialog.png deleted file mode 100644 index 1175d8e723..0000000000 Binary files a/docs/assets/images/image_upload_dialog.png and /dev/null differ diff --git a/docs/assets/images/k-means-cluster.png b/docs/assets/images/k-means-cluster.png deleted file mode 100644 index 402aed447d..0000000000 Binary files a/docs/assets/images/k-means-cluster.png and /dev/null differ diff --git a/docs/assets/images/kinship-inbreeding.png b/docs/assets/images/kinship-inbreeding.png deleted file mode 100644 index 6e44141ab4..0000000000 Binary files a/docs/assets/images/kinship-inbreeding.png and /dev/null differ diff --git a/docs/assets/images/list_manager_add_items.png b/docs/assets/images/list_manager_add_items.png deleted file mode 100644 index 6e039b01a7..0000000000 Binary files a/docs/assets/images/list_manager_add_items.png and /dev/null differ diff --git a/docs/assets/images/list_manager_added_items.png b/docs/assets/images/list_manager_added_items.png deleted file mode 100644 index 8fadffec0b..0000000000 Binary files a/docs/assets/images/list_manager_added_items.png and /dev/null differ diff --git a/docs/assets/images/list_manager_list_types.png b/docs/assets/images/list_manager_list_types.png deleted file mode 100644 index 74b475bd4a..0000000000 Binary files a/docs/assets/images/list_manager_list_types.png and /dev/null differ diff --git a/docs/assets/images/list_manager_new_list.png b/docs/assets/images/list_manager_new_list.png deleted file mode 100644 index fbd150b509..0000000000 Binary files a/docs/assets/images/list_manager_new_list.png and /dev/null differ diff --git a/docs/assets/images/list_manager_start.png b/docs/assets/images/list_manager_start.png deleted file mode 100644 index 85fa86f5fb..0000000000 Binary files a/docs/assets/images/list_manager_start.png and /dev/null differ diff --git a/docs/assets/images/list_manager_view_list.png b/docs/assets/images/list_manager_view_list.png deleted file mode 100644 index e41ff230bd..0000000000 Binary files a/docs/assets/images/list_manager_view_list.png and /dev/null differ diff --git a/docs/assets/images/location_map.png b/docs/assets/images/location_map.png deleted file mode 100644 index 1f6283c606..0000000000 Binary files a/docs/assets/images/location_map.png and /dev/null differ diff --git a/docs/assets/images/manage_NIRS_cv.png b/docs/assets/images/manage_NIRS_cv.png deleted file mode 100644 index 25e92ce483..0000000000 Binary files a/docs/assets/images/manage_NIRS_cv.png and /dev/null differ diff --git a/docs/assets/images/manage_NIRS_export_model.png b/docs/assets/images/manage_NIRS_export_model.png deleted file mode 100644 index 7e2b044152..0000000000 Binary files a/docs/assets/images/manage_NIRS_export_model.png and /dev/null differ diff --git a/docs/assets/images/manage_NIRS_main_display.png b/docs/assets/images/manage_NIRS_main_display.png deleted file mode 100644 index 86e96e7973..0000000000 Binary files a/docs/assets/images/manage_NIRS_main_display.png and /dev/null differ diff --git a/docs/assets/images/manage_NIRS_prediction_results.png b/docs/assets/images/manage_NIRS_prediction_results.png deleted file mode 100644 index 8f2acd3632..0000000000 Binary files a/docs/assets/images/manage_NIRS_prediction_results.png and /dev/null differ diff --git a/docs/assets/images/manage_NIRS_prediction_workflow_dataset.png b/docs/assets/images/manage_NIRS_prediction_workflow_dataset.png deleted file mode 100644 index 91d8f9a5a3..0000000000 Binary files a/docs/assets/images/manage_NIRS_prediction_workflow_dataset.png and /dev/null differ diff --git a/docs/assets/images/manage_NIRS_prediction_workflow_intro.png b/docs/assets/images/manage_NIRS_prediction_workflow_intro.png deleted file mode 100644 index d6e9d1d701..0000000000 Binary files a/docs/assets/images/manage_NIRS_prediction_workflow_intro.png and /dev/null differ diff --git a/docs/assets/images/manage_NIRS_select_model.png b/docs/assets/images/manage_NIRS_select_model.png deleted file mode 100644 index a6a52bed9c..0000000000 Binary files a/docs/assets/images/manage_NIRS_select_model.png and /dev/null differ diff --git a/docs/assets/images/manage_NIRS_snv.png b/docs/assets/images/manage_NIRS_snv.png deleted file mode 100644 index ad8b6d8240..0000000000 Binary files a/docs/assets/images/manage_NIRS_snv.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_complete_using_file.png b/docs/assets/images/manage_accessions_add_accessions_complete_using_file.png deleted file mode 100644 index d05d1c5d5a..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_complete_using_file.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_complete_using_list.png b/docs/assets/images/manage_accessions_add_accessions_complete_using_list.png deleted file mode 100644 index 02de06670d..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_complete_using_list.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_found.png b/docs/assets/images/manage_accessions_add_accessions_found.png deleted file mode 100644 index 247ec13606..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_found.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_fuzzy.png b/docs/assets/images/manage_accessions_add_accessions_fuzzy.png deleted file mode 100644 index 314d1a994b..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_fuzzy.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_fuzzy_options.png b/docs/assets/images/manage_accessions_add_accessions_fuzzy_options.png deleted file mode 100644 index 44cbbb4f9c..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_fuzzy_options.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_link.png b/docs/assets/images/manage_accessions_add_accessions_link.png deleted file mode 100644 index 7a8b0119e9..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_link.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_saved.png b/docs/assets/images/manage_accessions_add_accessions_saved.png deleted file mode 100644 index aaa65bc317..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_saved.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_spreadsheet_info.png b/docs/assets/images/manage_accessions_add_accessions_spreadsheet_info.png deleted file mode 100644 index e87c20ab51..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_spreadsheet_info.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_using_file.png b/docs/assets/images/manage_accessions_add_accessions_using_file.png deleted file mode 100644 index 17974c6709..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_using_file.png and /dev/null differ diff --git a/docs/assets/images/manage_accessions_add_accessions_using_lists.png b/docs/assets/images/manage_accessions_add_accessions_using_lists.png deleted file mode 100644 index b6d0933106..0000000000 Binary files a/docs/assets/images/manage_accessions_add_accessions_using_lists.png and /dev/null differ diff --git a/docs/assets/images/manage_cross_click_cross_wishlist.png b/docs/assets/images/manage_cross_click_cross_wishlist.png deleted file mode 100644 index 53520e7fa6..0000000000 Binary files a/docs/assets/images/manage_cross_click_cross_wishlist.png and /dev/null differ diff --git a/docs/assets/images/manage_crosses1.png b/docs/assets/images/manage_crosses1.png deleted file mode 100644 index 0399c65e82..0000000000 Binary files a/docs/assets/images/manage_crosses1.png and /dev/null differ diff --git a/docs/assets/images/manage_crosses2.png b/docs/assets/images/manage_crosses2.png deleted file mode 100644 index 99c4efd6a9..0000000000 Binary files a/docs/assets/images/manage_crosses2.png and /dev/null differ diff --git a/docs/assets/images/manage_crosses_addcross.png b/docs/assets/images/manage_crosses_addcross.png deleted file mode 100644 index eef2532f9c..0000000000 Binary files a/docs/assets/images/manage_crosses_addcross.png and /dev/null differ diff --git a/docs/assets/images/manage_crosses_crossingtrial.png b/docs/assets/images/manage_crosses_crossingtrial.png deleted file mode 100644 index 4163afa0a4..0000000000 Binary files a/docs/assets/images/manage_crosses_crossingtrial.png and /dev/null differ diff --git a/docs/assets/images/manage_crosses_upload.png b/docs/assets/images/manage_crosses_upload.png deleted file mode 100644 index ece8c15cb8..0000000000 Binary files a/docs/assets/images/manage_crosses_upload.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_data_upload_button.png b/docs/assets/images/manage_genotyping_data_upload_button.png deleted file mode 100644 index 292054a2ac..0000000000 Binary files a/docs/assets/images/manage_genotyping_data_upload_button.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_data_upload_dialog_intro.png b/docs/assets/images/manage_genotyping_data_upload_dialog_intro.png deleted file mode 100644 index 4881cafdd2..0000000000 Binary files a/docs/assets/images/manage_genotyping_data_upload_dialog_intro.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_data_upload_dialog_project.png b/docs/assets/images/manage_genotyping_data_upload_dialog_project.png deleted file mode 100644 index 663c42af78..0000000000 Binary files a/docs/assets/images/manage_genotyping_data_upload_dialog_project.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_data_upload_dialog_protocol.png b/docs/assets/images/manage_genotyping_data_upload_dialog_protocol.png deleted file mode 100644 index 9f98f3601e..0000000000 Binary files a/docs/assets/images/manage_genotyping_data_upload_dialog_protocol.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_data_upload_dialog_vcf.png b/docs/assets/images/manage_genotyping_data_upload_dialog_vcf.png deleted file mode 100644 index 3816349b63..0000000000 Binary files a/docs/assets/images/manage_genotyping_data_upload_dialog_vcf.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_data_wizard_download_prot.png b/docs/assets/images/manage_genotyping_data_wizard_download_prot.png deleted file mode 100644 index 6f5fadfb31..0000000000 Binary files a/docs/assets/images/manage_genotyping_data_wizard_download_prot.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_data_wizard_trial.png b/docs/assets/images/manage_genotyping_data_wizard_trial.png deleted file mode 100644 index 3cb2661ef1..0000000000 Binary files a/docs/assets/images/manage_genotyping_data_wizard_trial.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_trials.png b/docs/assets/images/manage_genotyping_trials.png deleted file mode 100644 index fa4db1c5d0..0000000000 Binary files a/docs/assets/images/manage_genotyping_trials.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_trials_add_trial.png b/docs/assets/images/manage_genotyping_trials_add_trial.png deleted file mode 100644 index 97914dca99..0000000000 Binary files a/docs/assets/images/manage_genotyping_trials_add_trial.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_trials_add_trial_confirm.png b/docs/assets/images/manage_genotyping_trials_add_trial_confirm.png deleted file mode 100644 index 8e77c1fc1b..0000000000 Binary files a/docs/assets/images/manage_genotyping_trials_add_trial_confirm.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_trials_add_trial_wells.png b/docs/assets/images/manage_genotyping_trials_add_trial_wells.png deleted file mode 100644 index f7b15f8b84..0000000000 Binary files a/docs/assets/images/manage_genotyping_trials_add_trial_wells.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_trials_detail_info.png b/docs/assets/images/manage_genotyping_trials_detail_info.png deleted file mode 100644 index a0caac3049..0000000000 Binary files a/docs/assets/images/manage_genotyping_trials_detail_info.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_trials_detail_table.png b/docs/assets/images/manage_genotyping_trials_detail_table.png deleted file mode 100644 index b5bfee2e53..0000000000 Binary files a/docs/assets/images/manage_genotyping_trials_detail_table.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_trials_expand.png b/docs/assets/images/manage_genotyping_trials_expand.png deleted file mode 100644 index 19d0d2143b..0000000000 Binary files a/docs/assets/images/manage_genotyping_trials_expand.png and /dev/null differ diff --git a/docs/assets/images/manage_genotyping_trials_upload_template.png b/docs/assets/images/manage_genotyping_trials_upload_template.png deleted file mode 100644 index 7b52c36662..0000000000 Binary files a/docs/assets/images/manage_genotyping_trials_upload_template.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_apply.png b/docs/assets/images/manage_image_phenotyping_standard_process_apply.png deleted file mode 100644 index eda704ba07..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_apply.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_button.png b/docs/assets/images/manage_image_phenotyping_standard_process_button.png deleted file mode 100644 index b5598ccff2..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_button.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_cropping.png b/docs/assets/images/manage_image_phenotyping_standard_process_cropping.png deleted file mode 100644 index dc6fed6107..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_cropping.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_drone_run_band.png b/docs/assets/images/manage_image_phenotyping_standard_process_drone_run_band.png deleted file mode 100644 index 1e90819cc5..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_drone_run_band.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_intro.png b/docs/assets/images/manage_image_phenotyping_standard_process_intro.png deleted file mode 100644 index 066e087c18..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_intro.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_phenotypes.png b/docs/assets/images/manage_image_phenotyping_standard_process_phenotypes.png deleted file mode 100644 index c91faad77d..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_phenotypes.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_plot_polygon.png b/docs/assets/images/manage_image_phenotyping_standard_process_plot_polygon.png deleted file mode 100644 index 124f980289..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_plot_polygon.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_rotate.png b/docs/assets/images/manage_image_phenotyping_standard_process_rotate.png deleted file mode 100644 index 0d6c81305f..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_rotate.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_threshold_hist.png b/docs/assets/images/manage_image_phenotyping_standard_process_threshold_hist.png deleted file mode 100644 index 76ee2c1a1d..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_threshold_hist.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_standard_process_vegetation_index.png b/docs/assets/images/manage_image_phenotyping_standard_process_vegetation_index.png deleted file mode 100644 index 779728165e..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_standard_process_vegetation_index.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_upload_drone_run.png b/docs/assets/images/manage_image_phenotyping_upload_drone_run.png deleted file mode 100644 index 155527b1be..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_upload_drone_run.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_upload_field_trial.png b/docs/assets/images/manage_image_phenotyping_upload_field_trial.png deleted file mode 100644 index df5781b2a1..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_upload_field_trial.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_upload_image_info_1.png b/docs/assets/images/manage_image_phenotyping_upload_image_info_1.png deleted file mode 100644 index 126dea631b..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_upload_image_info_1.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_upload_image_info_2.png b/docs/assets/images/manage_image_phenotyping_upload_image_info_2.png deleted file mode 100644 index 655db9603c..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_upload_image_info_2.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_upload_image_info_3.png b/docs/assets/images/manage_image_phenotyping_upload_image_info_3.png deleted file mode 100644 index e24d606312..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_upload_image_info_3.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_upload_images_rasters.png b/docs/assets/images/manage_image_phenotyping_upload_images_rasters.png deleted file mode 100644 index dc09d0d0d1..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_upload_images_rasters.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_upload_images_zipfile.png b/docs/assets/images/manage_image_phenotyping_upload_images_zipfile.png deleted file mode 100644 index a3ca997d82..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_upload_images_zipfile.png and /dev/null differ diff --git a/docs/assets/images/manage_image_phenotyping_upload_intro.png b/docs/assets/images/manage_image_phenotyping_upload_intro.png deleted file mode 100644 index ee8d1ed394..0000000000 Binary files a/docs/assets/images/manage_image_phenotyping_upload_intro.png and /dev/null differ diff --git a/docs/assets/images/manage_observation_variables_start_button.png b/docs/assets/images/manage_observation_variables_start_button.png deleted file mode 100644 index 68d63ea624..0000000000 Binary files a/docs/assets/images/manage_observation_variables_start_button.png and /dev/null differ diff --git a/docs/assets/images/manage_observation_variables_workflow_intro.png b/docs/assets/images/manage_observation_variables_workflow_intro.png deleted file mode 100644 index fc5df2770f..0000000000 Binary files a/docs/assets/images/manage_observation_variables_workflow_intro.png and /dev/null differ diff --git a/docs/assets/images/manage_observation_variables_workflow_method.png b/docs/assets/images/manage_observation_variables_workflow_method.png deleted file mode 100644 index b596abddb1..0000000000 Binary files a/docs/assets/images/manage_observation_variables_workflow_method.png and /dev/null differ diff --git a/docs/assets/images/manage_observation_variables_workflow_observation_variable.png b/docs/assets/images/manage_observation_variables_workflow_observation_variable.png deleted file mode 100644 index bef57ebf6e..0000000000 Binary files a/docs/assets/images/manage_observation_variables_workflow_observation_variable.png and /dev/null differ diff --git a/docs/assets/images/manage_observation_variables_workflow_scale.png b/docs/assets/images/manage_observation_variables_workflow_scale.png deleted file mode 100644 index b123feffd8..0000000000 Binary files a/docs/assets/images/manage_observation_variables_workflow_scale.png and /dev/null differ diff --git a/docs/assets/images/manage_observation_variables_workflow_submit.png b/docs/assets/images/manage_observation_variables_workflow_submit.png deleted file mode 100644 index 1115683746..0000000000 Binary files a/docs/assets/images/manage_observation_variables_workflow_submit.png and /dev/null differ diff --git a/docs/assets/images/manage_observation_variables_workflow_trait.png b/docs/assets/images/manage_observation_variables_workflow_trait.png deleted file mode 100644 index 52f93ce544..0000000000 Binary files a/docs/assets/images/manage_observation_variables_workflow_trait.png and /dev/null differ diff --git a/docs/assets/images/manage_populations.png b/docs/assets/images/manage_populations.png deleted file mode 100644 index f4993eb4c5..0000000000 Binary files a/docs/assets/images/manage_populations.png and /dev/null differ diff --git a/docs/assets/images/manage_populations_add_population.png b/docs/assets/images/manage_populations_add_population.png deleted file mode 100644 index 5c6bcd1fa6..0000000000 Binary files a/docs/assets/images/manage_populations_add_population.png and /dev/null differ diff --git a/docs/assets/images/manage_populations_expand_table.png b/docs/assets/images/manage_populations_expand_table.png deleted file mode 100644 index e292627c59..0000000000 Binary files a/docs/assets/images/manage_populations_expand_table.png and /dev/null differ diff --git a/docs/assets/images/manage_populations_table.png b/docs/assets/images/manage_populations_table.png deleted file mode 100644 index 064260ec0e..0000000000 Binary files a/docs/assets/images/manage_populations_table.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlot_accession_detail.png b/docs/assets/images/manage_seedlot_accession_detail.png deleted file mode 100644 index f399ac437f..0000000000 Binary files a/docs/assets/images/manage_seedlot_accession_detail.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlot_accession_list_search.png b/docs/assets/images/manage_seedlot_accession_list_search.png deleted file mode 100644 index b841ddbeae..0000000000 Binary files a/docs/assets/images/manage_seedlot_accession_list_search.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlot_add_seedlot.png b/docs/assets/images/manage_seedlot_add_seedlot.png deleted file mode 100644 index f5c6a7b722..0000000000 Binary files a/docs/assets/images/manage_seedlot_add_seedlot.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlot_list_available_seedlots.png b/docs/assets/images/manage_seedlot_list_available_seedlots.png deleted file mode 100644 index 83f289041e..0000000000 Binary files a/docs/assets/images/manage_seedlot_list_available_seedlots.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlot_upload_inventory.png b/docs/assets/images/manage_seedlot_upload_inventory.png deleted file mode 100644 index 601ad72fe8..0000000000 Binary files a/docs/assets/images/manage_seedlot_upload_inventory.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlot_upload_inventory_template.png b/docs/assets/images/manage_seedlot_upload_inventory_template.png deleted file mode 100644 index 1188565c28..0000000000 Binary files a/docs/assets/images/manage_seedlot_upload_inventory_template.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlot_upload_seedlots.png b/docs/assets/images/manage_seedlot_upload_seedlots.png deleted file mode 100644 index c8345df07e..0000000000 Binary files a/docs/assets/images/manage_seedlot_upload_seedlots.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlots.png b/docs/assets/images/manage_seedlots.png deleted file mode 100644 index 6071a006ee..0000000000 Binary files a/docs/assets/images/manage_seedlots.png and /dev/null differ diff --git a/docs/assets/images/manage_seedlots_seedlot_maintenance.png b/docs/assets/images/manage_seedlots_seedlot_maintenance.png deleted file mode 100644 index 0c849fa69b..0000000000 Binary files a/docs/assets/images/manage_seedlots_seedlot_maintenance.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_button.png b/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_button.png deleted file mode 100644 index 0e7641195a..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_button.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_download.png b/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_download.png deleted file mode 100644 index 7eac3b7c4d..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_download.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_intro.png b/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_intro.png deleted file mode 100644 index 5d6e500961..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_intro.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_num_plants.png b/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_num_plants.png deleted file mode 100644 index 481ef48f44..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_num_plants.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_num_tissues.png b/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_num_tissues.png deleted file mode 100644 index 069cdddb09..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_num_tissues.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_select.png b/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_select.png deleted file mode 100644 index 5420ad95ec..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_select.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_submit.png b/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_submit.png deleted file mode 100644 index e062f6060f..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_field_trial_submit.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_genotyping_trial_button.png b/docs/assets/images/manage_tissue_samples_create_tissues_genotyping_trial_button.png deleted file mode 100644 index c3574146dc..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_genotyping_trial_button.png and /dev/null differ diff --git a/docs/assets/images/manage_tissue_samples_create_tissues_genotyping_trial_download.png b/docs/assets/images/manage_tissue_samples_create_tissues_genotyping_trial_download.png deleted file mode 100644 index 16b3d5a274..0000000000 Binary files a/docs/assets/images/manage_tissue_samples_create_tissues_genotyping_trial_download.png and /dev/null differ diff --git a/docs/assets/images/manage_trials.png b/docs/assets/images/manage_trials.png deleted file mode 100644 index 1388570f9c..0000000000 Binary files a/docs/assets/images/manage_trials.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_tissue_sample_create.png b/docs/assets/images/manage_trials_tissue_sample_create.png deleted file mode 100644 index f60504aed4..0000000000 Binary files a/docs/assets/images/manage_trials_tissue_sample_create.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_tissue_sample_default.png b/docs/assets/images/manage_trials_tissue_sample_default.png deleted file mode 100644 index 17e445789e..0000000000 Binary files a/docs/assets/images/manage_trials_tissue_sample_default.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_tissue_sample_detail.png b/docs/assets/images/manage_trials_tissue_sample_detail.png deleted file mode 100644 index bb187f48bb..0000000000 Binary files a/docs/assets/images/manage_trials_tissue_sample_detail.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_tissue_sample_related_stock.png b/docs/assets/images/manage_trials_tissue_sample_related_stock.png deleted file mode 100644 index 8315708f36..0000000000 Binary files a/docs/assets/images/manage_trials_tissue_sample_related_stock.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_tissue_samples.png b/docs/assets/images/manage_trials_tissue_samples.png deleted file mode 100644 index 56255706c9..0000000000 Binary files a/docs/assets/images/manage_trials_tissue_samples.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_1.png b/docs/assets/images/manage_trials_upload_trial_1.png deleted file mode 100644 index 98636bec52..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_1.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_2.png b/docs/assets/images/manage_trials_upload_trial_2.png deleted file mode 100644 index 06e95f323d..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_2.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_3.png b/docs/assets/images/manage_trials_upload_trial_3.png deleted file mode 100644 index a9d23c486d..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_3.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_4.png b/docs/assets/images/manage_trials_upload_trial_4.png deleted file mode 100644 index d5ac03dec0..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_4.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_5.png b/docs/assets/images/manage_trials_upload_trial_5.png deleted file mode 100644 index ef4d8bec05..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_5.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_6.png b/docs/assets/images/manage_trials_upload_trial_6.png deleted file mode 100644 index 0766081d47..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_6.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_7.png b/docs/assets/images/manage_trials_upload_trial_7.png deleted file mode 100644 index 82f077e640..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_7.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_8.png b/docs/assets/images/manage_trials_upload_trial_8.png deleted file mode 100644 index 8f9a36dfc0..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_8.png and /dev/null differ diff --git a/docs/assets/images/manage_trials_upload_trial_template.png b/docs/assets/images/manage_trials_upload_trial_template.png deleted file mode 100644 index 70b95f6316..0000000000 Binary files a/docs/assets/images/manage_trials_upload_trial_template.png and /dev/null differ diff --git a/docs/assets/images/manage_user_roles_page.png b/docs/assets/images/manage_user_roles_page.png deleted file mode 100644 index 6372d1f0f3..0000000000 Binary files a/docs/assets/images/manage_user_roles_page.png and /dev/null differ diff --git a/docs/assets/images/mixedmodel_tool_model_build_step.png b/docs/assets/images/mixedmodel_tool_model_build_step.png deleted file mode 100644 index d97a24f3b1..0000000000 Binary files a/docs/assets/images/mixedmodel_tool_model_build_step.png and /dev/null differ diff --git a/docs/assets/images/multiple_trial_upload_with_email.png b/docs/assets/images/multiple_trial_upload_with_email.png deleted file mode 100644 index df9571559a..0000000000 Binary files a/docs/assets/images/multiple_trial_upload_with_email.png and /dev/null differ diff --git a/docs/assets/images/new_cross_dialog2.png b/docs/assets/images/new_cross_dialog2.png deleted file mode 100644 index 619abfe947..0000000000 Binary files a/docs/assets/images/new_cross_dialog2.png and /dev/null differ diff --git a/docs/assets/images/new_cross_dialog_type.png b/docs/assets/images/new_cross_dialog_type.png deleted file mode 100644 index 6e346fd048..0000000000 Binary files a/docs/assets/images/new_cross_dialog_type.png and /dev/null differ diff --git a/docs/assets/images/new_program_screenshot.png b/docs/assets/images/new_program_screenshot.png deleted file mode 100644 index 812582a64a..0000000000 Binary files a/docs/assets/images/new_program_screenshot.png and /dev/null differ diff --git a/docs/assets/images/odk_ona_cross_progress_graph.png b/docs/assets/images/odk_ona_cross_progress_graph.png deleted file mode 100644 index f16a61381e..0000000000 Binary files a/docs/assets/images/odk_ona_cross_progress_graph.png and /dev/null differ diff --git a/docs/assets/images/odk_ona_cross_progress_tree.png b/docs/assets/images/odk_ona_cross_progress_tree.png deleted file mode 100644 index e8f38267fa..0000000000 Binary files a/docs/assets/images/odk_ona_cross_progress_tree.png and /dev/null differ diff --git a/docs/assets/images/odk_ona_cross_status.png b/docs/assets/images/odk_ona_cross_status.png deleted file mode 100644 index d3038d9631..0000000000 Binary files a/docs/assets/images/odk_ona_cross_status.png and /dev/null differ diff --git a/docs/assets/images/odk_ona_management.png b/docs/assets/images/odk_ona_management.png deleted file mode 100644 index 988c5faabf..0000000000 Binary files a/docs/assets/images/odk_ona_management.png and /dev/null differ diff --git a/docs/assets/images/ontology_browser.png b/docs/assets/images/ontology_browser.png deleted file mode 100644 index a28e7b66d4..0000000000 Binary files a/docs/assets/images/ontology_browser.png and /dev/null differ diff --git a/docs/assets/images/ontology_browser_variable.png b/docs/assets/images/ontology_browser_variable.png deleted file mode 100644 index 2ae6369801..0000000000 Binary files a/docs/assets/images/ontology_browser_variable.png and /dev/null differ diff --git a/docs/assets/images/outliers_dataset_actions.png b/docs/assets/images/outliers_dataset_actions.png deleted file mode 100644 index 19c00444d2..0000000000 Binary files a/docs/assets/images/outliers_dataset_actions.png and /dev/null differ diff --git a/docs/assets/images/outliers_dataset_basic_panel.png b/docs/assets/images/outliers_dataset_basic_panel.png deleted file mode 100644 index db3d362172..0000000000 Binary files a/docs/assets/images/outliers_dataset_basic_panel.png and /dev/null differ diff --git a/docs/assets/images/outliers_dataset_full_visualisation.png b/docs/assets/images/outliers_dataset_full_visualisation.png deleted file mode 100644 index cf3e27b67d..0000000000 Binary files a/docs/assets/images/outliers_dataset_full_visualisation.png and /dev/null differ diff --git a/docs/assets/images/outliers_dataset_icon.png b/docs/assets/images/outliers_dataset_icon.png deleted file mode 100644 index 46c906ab82..0000000000 Binary files a/docs/assets/images/outliers_dataset_icon.png and /dev/null differ diff --git a/docs/assets/images/outliers_dataset_panel_with_set.png b/docs/assets/images/outliers_dataset_panel_with_set.png deleted file mode 100644 index c9ea942e10..0000000000 Binary files a/docs/assets/images/outliers_dataset_panel_with_set.png and /dev/null differ diff --git a/docs/assets/images/pca_iita_naccri_trials.png b/docs/assets/images/pca_iita_naccri_trials.png deleted file mode 100644 index d299d99d24..0000000000 Binary files a/docs/assets/images/pca_iita_naccri_trials.png and /dev/null differ diff --git a/docs/assets/images/pedigree_upload_format.png b/docs/assets/images/pedigree_upload_format.png deleted file mode 100644 index 624aed2591..0000000000 Binary files a/docs/assets/images/pedigree_upload_format.png and /dev/null differ diff --git a/docs/assets/images/phenotype_spreadsheet_upload_dialog.png b/docs/assets/images/phenotype_spreadsheet_upload_dialog.png deleted file mode 100644 index bd9804071f..0000000000 Binary files a/docs/assets/images/phenotype_spreadsheet_upload_dialog.png and /dev/null differ diff --git a/docs/assets/images/phenotype_spreadsheet_upload_help_dialog.png b/docs/assets/images/phenotype_spreadsheet_upload_help_dialog.png deleted file mode 100644 index 7da6bd84c2..0000000000 Binary files a/docs/assets/images/phenotype_spreadsheet_upload_help_dialog.png and /dev/null differ diff --git a/docs/assets/images/plot_pheno_barcode.png b/docs/assets/images/plot_pheno_barcode.png deleted file mode 100644 index 14d71869c7..0000000000 Binary files a/docs/assets/images/plot_pheno_barcode.png and /dev/null differ diff --git a/docs/assets/images/progenies_upload_spreadsheet.png b/docs/assets/images/progenies_upload_spreadsheet.png deleted file mode 100644 index f44f5ef71a..0000000000 Binary files a/docs/assets/images/progenies_upload_spreadsheet.png and /dev/null differ diff --git a/docs/assets/images/replace_plot_accession_form.png b/docs/assets/images/replace_plot_accession_form.png deleted file mode 100644 index 970cff2460..0000000000 Binary files a/docs/assets/images/replace_plot_accession_form.png and /dev/null differ diff --git a/docs/assets/images/roles.png b/docs/assets/images/roles.png deleted file mode 100644 index 1b98be0580..0000000000 Binary files a/docs/assets/images/roles.png and /dev/null differ diff --git a/docs/assets/images/search_accessions.png b/docs/assets/images/search_accessions.png deleted file mode 100644 index 168dfd2711..0000000000 Binary files a/docs/assets/images/search_accessions.png and /dev/null differ diff --git a/docs/assets/images/search_accessions_graphical_filtering.png b/docs/assets/images/search_accessions_graphical_filtering.png deleted file mode 100644 index a75823cf06..0000000000 Binary files a/docs/assets/images/search_accessions_graphical_filtering.png and /dev/null differ diff --git a/docs/assets/images/search_accessions_properties_search.png b/docs/assets/images/search_accessions_properties_search.png deleted file mode 100644 index 7275f53b13..0000000000 Binary files a/docs/assets/images/search_accessions_properties_search.png and /dev/null differ diff --git a/docs/assets/images/search_accessions_properties_view.png b/docs/assets/images/search_accessions_properties_view.png deleted file mode 100644 index 606d22be85..0000000000 Binary files a/docs/assets/images/search_accessions_properties_view.png and /dev/null differ diff --git a/docs/assets/images/search_seedlots.png b/docs/assets/images/search_seedlots.png deleted file mode 100644 index e0b79c1823..0000000000 Binary files a/docs/assets/images/search_seedlots.png and /dev/null differ diff --git a/docs/assets/images/search_wizard_genotype_analyses_grm.png b/docs/assets/images/search_wizard_genotype_analyses_grm.png deleted file mode 100644 index def7d18451..0000000000 Binary files a/docs/assets/images/search_wizard_genotype_analyses_grm.png and /dev/null differ diff --git a/docs/assets/images/search_wizard_genotype_analyses_gwas.png b/docs/assets/images/search_wizard_genotype_analyses_gwas.png deleted file mode 100644 index 6bad710e1a..0000000000 Binary files a/docs/assets/images/search_wizard_genotype_analyses_gwas.png and /dev/null differ diff --git a/docs/assets/images/search_wizard_genotype_analyses_manhattan_plot.png b/docs/assets/images/search_wizard_genotype_analyses_manhattan_plot.png deleted file mode 100644 index cb7e19c1bd..0000000000 Binary files a/docs/assets/images/search_wizard_genotype_analyses_manhattan_plot.png and /dev/null differ diff --git a/docs/assets/images/search_wizard_genotype_analyses_qq_plot.png b/docs/assets/images/search_wizard_genotype_analyses_qq_plot.png deleted file mode 100644 index 0584d38971..0000000000 Binary files a/docs/assets/images/search_wizard_genotype_analyses_qq_plot.png and /dev/null differ diff --git a/docs/assets/images/search_wizard_genotyping_protocols_download_genotypes.png b/docs/assets/images/search_wizard_genotyping_protocols_download_genotypes.png deleted file mode 100644 index 4f3a5a5f58..0000000000 Binary files a/docs/assets/images/search_wizard_genotyping_protocols_download_genotypes.png and /dev/null differ diff --git a/docs/assets/images/search_wizard_genotyping_protocols_download_main.png b/docs/assets/images/search_wizard_genotyping_protocols_download_main.png deleted file mode 100644 index 71deddbe73..0000000000 Binary files a/docs/assets/images/search_wizard_genotyping_protocols_download_main.png and /dev/null differ diff --git a/docs/assets/images/search_wizard_genotyping_protocols_download_phenotypes.png b/docs/assets/images/search_wizard_genotyping_protocols_download_phenotypes.png deleted file mode 100644 index 1baa824e12..0000000000 Binary files a/docs/assets/images/search_wizard_genotyping_protocols_download_phenotypes.png and /dev/null differ diff --git a/docs/assets/images/seedlot_add_new_transaction.png b/docs/assets/images/seedlot_add_new_transaction.png deleted file mode 100644 index 87ed2a92c1..0000000000 Binary files a/docs/assets/images/seedlot_add_new_transaction.png and /dev/null differ diff --git a/docs/assets/images/seedlot_detail_page.png b/docs/assets/images/seedlot_detail_page.png deleted file mode 100644 index 856e9d5dba..0000000000 Binary files a/docs/assets/images/seedlot_detail_page.png and /dev/null differ diff --git a/docs/assets/images/seedlot_maintenance_events_filtered.png b/docs/assets/images/seedlot_maintenance_events_filtered.png deleted file mode 100644 index afc2005c2b..0000000000 Binary files a/docs/assets/images/seedlot_maintenance_events_filtered.png and /dev/null differ diff --git a/docs/assets/images/seedlot_maintenance_events_unfiltered.png b/docs/assets/images/seedlot_maintenance_events_unfiltered.png deleted file mode 100644 index d4d18ba25f..0000000000 Binary files a/docs/assets/images/seedlot_maintenance_events_unfiltered.png and /dev/null differ diff --git a/docs/assets/images/seedlot_maintenance_record.png b/docs/assets/images/seedlot_maintenance_record.png deleted file mode 100644 index 135f0b8a80..0000000000 Binary files a/docs/assets/images/seedlot_maintenance_record.png and /dev/null differ diff --git a/docs/assets/images/seedlot_maintenance_upload.png b/docs/assets/images/seedlot_maintenance_upload.png deleted file mode 100644 index 5c31f08875..0000000000 Binary files a/docs/assets/images/seedlot_maintenance_upload.png and /dev/null differ diff --git a/docs/assets/images/selection_proportion_clustering.png b/docs/assets/images/selection_proportion_clustering.png deleted file mode 100644 index f8f331bcdf..0000000000 Binary files a/docs/assets/images/selection_proportion_clustering.png and /dev/null differ diff --git a/docs/assets/images/sequence_metadata_manage.png b/docs/assets/images/sequence_metadata_manage.png deleted file mode 100644 index c6522103dc..0000000000 Binary files a/docs/assets/images/sequence_metadata_manage.png and /dev/null differ diff --git a/docs/assets/images/sequence_metadata_search_advanced.png b/docs/assets/images/sequence_metadata_search_advanced.png deleted file mode 100644 index ec052e4e6c..0000000000 Binary files a/docs/assets/images/sequence_metadata_search_advanced.png and /dev/null differ diff --git a/docs/assets/images/sequence_metadata_search_basic.png b/docs/assets/images/sequence_metadata_search_basic.png deleted file mode 100644 index 30318e26af..0000000000 Binary files a/docs/assets/images/sequence_metadata_search_basic.png and /dev/null differ diff --git a/docs/assets/images/sequence_metadata_search_results.png b/docs/assets/images/sequence_metadata_search_results.png deleted file mode 100644 index ededb22b20..0000000000 Binary files a/docs/assets/images/sequence_metadata_search_results.png and /dev/null differ diff --git a/docs/assets/images/spatial_layout_usage_help.png b/docs/assets/images/spatial_layout_usage_help.png deleted file mode 100644 index 141765b324..0000000000 Binary files a/docs/assets/images/spatial_layout_usage_help.png and /dev/null differ diff --git a/docs/assets/images/spatial_layout_usage_help_1.png b/docs/assets/images/spatial_layout_usage_help_1.png deleted file mode 100644 index 5d68f69978..0000000000 Binary files a/docs/assets/images/spatial_layout_usage_help_1.png and /dev/null differ diff --git a/docs/assets/images/spatial_layout_usage_help_2.png b/docs/assets/images/spatial_layout_usage_help_2.png deleted file mode 100644 index 3fce0919f0..0000000000 Binary files a/docs/assets/images/spatial_layout_usage_help_2.png and /dev/null differ diff --git a/docs/assets/images/spatial_layout_usage_help_3.png b/docs/assets/images/spatial_layout_usage_help_3.png deleted file mode 100644 index 0aa1b38d48..0000000000 Binary files a/docs/assets/images/spatial_layout_usage_help_3.png and /dev/null differ diff --git a/docs/assets/images/stock_add_image.png b/docs/assets/images/stock_add_image.png deleted file mode 100644 index feb9dfa254..0000000000 Binary files a/docs/assets/images/stock_add_image.png and /dev/null differ diff --git a/docs/assets/images/tool_compatibility_data_summary.png b/docs/assets/images/tool_compatibility_data_summary.png deleted file mode 100644 index c0a2d97ed2..0000000000 Binary files a/docs/assets/images/tool_compatibility_data_summary.png and /dev/null differ diff --git a/docs/assets/images/tool_compatibility_details.png b/docs/assets/images/tool_compatibility_details.png deleted file mode 100644 index e4876d8a9c..0000000000 Binary files a/docs/assets/images/tool_compatibility_details.png and /dev/null differ diff --git a/docs/assets/images/trait-search-default.png b/docs/assets/images/trait-search-default.png deleted file mode 100644 index e81814011b..0000000000 Binary files a/docs/assets/images/trait-search-default.png and /dev/null differ diff --git a/docs/assets/images/trait-search.png b/docs/assets/images/trait-search.png deleted file mode 100644 index bc7da3f71d..0000000000 Binary files a/docs/assets/images/trait-search.png and /dev/null differ diff --git a/docs/assets/images/trial_barcode.png b/docs/assets/images/trial_barcode.png deleted file mode 100644 index 209493e6e9..0000000000 Binary files a/docs/assets/images/trial_barcode.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_1.png b/docs/assets/images/trial_create_form_1.png deleted file mode 100644 index 16a465448b..0000000000 Binary files a/docs/assets/images/trial_create_form_1.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_2.png b/docs/assets/images/trial_create_form_2.png deleted file mode 100644 index 6c0e80b973..0000000000 Binary files a/docs/assets/images/trial_create_form_2.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_3.png b/docs/assets/images/trial_create_form_3.png deleted file mode 100644 index 716f37e0c2..0000000000 Binary files a/docs/assets/images/trial_create_form_3.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_4.png b/docs/assets/images/trial_create_form_4.png deleted file mode 100644 index 7057b9ea7a..0000000000 Binary files a/docs/assets/images/trial_create_form_4.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_5.png b/docs/assets/images/trial_create_form_5.png deleted file mode 100644 index c544c04144..0000000000 Binary files a/docs/assets/images/trial_create_form_5.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_6.png b/docs/assets/images/trial_create_form_6.png deleted file mode 100644 index 80b405e5ba..0000000000 Binary files a/docs/assets/images/trial_create_form_6.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_6_bottom.png b/docs/assets/images/trial_create_form_6_bottom.png deleted file mode 100644 index ce10a0f747..0000000000 Binary files a/docs/assets/images/trial_create_form_6_bottom.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_6_middle.png b/docs/assets/images/trial_create_form_6_middle.png deleted file mode 100644 index 34ba171db2..0000000000 Binary files a/docs/assets/images/trial_create_form_6_middle.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_6_top.png b/docs/assets/images/trial_create_form_6_top.png deleted file mode 100644 index 02689c91ca..0000000000 Binary files a/docs/assets/images/trial_create_form_6_top.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_7.png b/docs/assets/images/trial_create_form_7.png deleted file mode 100644 index 34cbf4e4ea..0000000000 Binary files a/docs/assets/images/trial_create_form_7.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_7_bottom.png b/docs/assets/images/trial_create_form_7_bottom.png deleted file mode 100644 index ce10a0f747..0000000000 Binary files a/docs/assets/images/trial_create_form_7_bottom.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_7_middle.png b/docs/assets/images/trial_create_form_7_middle.png deleted file mode 100644 index 34ba171db2..0000000000 Binary files a/docs/assets/images/trial_create_form_7_middle.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_7_top.png b/docs/assets/images/trial_create_form_7_top.png deleted file mode 100644 index 98ad9300ff..0000000000 Binary files a/docs/assets/images/trial_create_form_7_top.png and /dev/null differ diff --git a/docs/assets/images/trial_create_form_8.png b/docs/assets/images/trial_create_form_8.png deleted file mode 100644 index 6583861ded..0000000000 Binary files a/docs/assets/images/trial_create_form_8.png and /dev/null differ diff --git a/docs/assets/images/trial_create_manage_trials.png b/docs/assets/images/trial_create_manage_trials.png deleted file mode 100644 index a02b812ae8..0000000000 Binary files a/docs/assets/images/trial_create_manage_trials.png and /dev/null differ diff --git a/docs/assets/images/trial_create_open_form.png b/docs/assets/images/trial_create_open_form.png deleted file mode 100644 index e9782cb87a..0000000000 Binary files a/docs/assets/images/trial_create_open_form.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_add_plant_entries.png b/docs/assets/images/trial_detail_page_add_plant_entries.png deleted file mode 100644 index 022eb51c1d..0000000000 Binary files a/docs/assets/images/trial_detail_page_add_plant_entries.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_add_plant_entries_autogenerated.png b/docs/assets/images/trial_detail_page_add_plant_entries_autogenerated.png deleted file mode 100644 index 96a5ac7d75..0000000000 Binary files a/docs/assets/images/trial_detail_page_add_plant_entries_autogenerated.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_add_plant_entries_upload.png b/docs/assets/images/trial_detail_page_add_plant_entries_upload.png deleted file mode 100644 index d95e484ffc..0000000000 Binary files a/docs/assets/images/trial_detail_page_add_plant_entries_upload.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_add_plant_entries_upload_info.png b/docs/assets/images/trial_detail_page_add_plant_entries_upload_info.png deleted file mode 100644 index 33f8110138..0000000000 Binary files a/docs/assets/images/trial_detail_page_add_plant_entries_upload_info.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_add_plot_gps.png b/docs/assets/images/trial_detail_page_add_plot_gps.png deleted file mode 100644 index b46775ca34..0000000000 Binary files a/docs/assets/images/trial_detail_page_add_plot_gps.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_add_plot_gps_dialog.png b/docs/assets/images/trial_detail_page_add_plot_gps_dialog.png deleted file mode 100644 index ef962f033e..0000000000 Binary files a/docs/assets/images/trial_detail_page_add_plot_gps_dialog.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_add_plot_gps_upload_info.png b/docs/assets/images/trial_detail_page_add_plot_gps_upload_info.png deleted file mode 100644 index 72e5f9b79e..0000000000 Binary files a/docs/assets/images/trial_detail_page_add_plot_gps_upload_info.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_delete_trait1.png b/docs/assets/images/trial_detail_page_delete_trait1.png deleted file mode 100644 index b39238e058..0000000000 Binary files a/docs/assets/images/trial_detail_page_delete_trait1.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_delete_trait2.png b/docs/assets/images/trial_detail_page_delete_trait2.png deleted file mode 100644 index 649491ccd6..0000000000 Binary files a/docs/assets/images/trial_detail_page_delete_trait2.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_delete_trait3.png b/docs/assets/images/trial_detail_page_delete_trait3.png deleted file mode 100644 index c859a8477b..0000000000 Binary files a/docs/assets/images/trial_detail_page_delete_trait3.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_navigator.png b/docs/assets/images/trial_detail_page_navigator.png deleted file mode 100644 index c8d856bb5f..0000000000 Binary files a/docs/assets/images/trial_detail_page_navigator.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_navigator_1.png b/docs/assets/images/trial_detail_page_navigator_1.png deleted file mode 100644 index 0c86fb6b52..0000000000 Binary files a/docs/assets/images/trial_detail_page_navigator_1.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_navigator_2.png b/docs/assets/images/trial_detail_page_navigator_2.png deleted file mode 100644 index 43f1f7b5bf..0000000000 Binary files a/docs/assets/images/trial_detail_page_navigator_2.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_navigator_3.png b/docs/assets/images/trial_detail_page_navigator_3.png deleted file mode 100644 index f70afc2eea..0000000000 Binary files a/docs/assets/images/trial_detail_page_navigator_3.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_start.png b/docs/assets/images/trial_detail_page_start.png deleted file mode 100644 index 71ae4ddc57..0000000000 Binary files a/docs/assets/images/trial_detail_page_start.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_suppress_phenotype.png b/docs/assets/images/trial_detail_page_suppress_phenotype.png deleted file mode 100644 index c7604288ca..0000000000 Binary files a/docs/assets/images/trial_detail_page_suppress_phenotype.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_trait_heatmap.png b/docs/assets/images/trial_detail_page_trait_heatmap.png deleted file mode 100644 index baee7c1529..0000000000 Binary files a/docs/assets/images/trial_detail_page_trait_heatmap.png and /dev/null differ diff --git a/docs/assets/images/trial_detail_page_view_repetitive_measurements.png b/docs/assets/images/trial_detail_page_view_repetitive_measurements.png deleted file mode 100644 index 903be8d86b..0000000000 Binary files a/docs/assets/images/trial_detail_page_view_repetitive_measurements.png and /dev/null differ diff --git a/docs/assets/images/trial_meta_data_download.png b/docs/assets/images/trial_meta_data_download.png deleted file mode 100644 index d9abb89ebf..0000000000 Binary files a/docs/assets/images/trial_meta_data_download.png and /dev/null differ diff --git a/docs/assets/images/trial_search.png b/docs/assets/images/trial_search.png deleted file mode 100644 index 5ce8d8f394..0000000000 Binary files a/docs/assets/images/trial_search.png and /dev/null differ diff --git a/docs/assets/images/trial_upload_additional_file.png b/docs/assets/images/trial_upload_additional_file.png deleted file mode 100644 index ac379ca729..0000000000 Binary files a/docs/assets/images/trial_upload_additional_file.png and /dev/null differ diff --git a/docs/assets/images/trial_upload_additional_file_info.png b/docs/assets/images/trial_upload_additional_file_info.png deleted file mode 100644 index 15e3cc48fb..0000000000 Binary files a/docs/assets/images/trial_upload_additional_file_info.png and /dev/null differ diff --git a/docs/assets/images/update_trial_metadata.png b/docs/assets/images/update_trial_metadata.png deleted file mode 100644 index eb04c25b21..0000000000 Binary files a/docs/assets/images/update_trial_metadata.png and /dev/null differ diff --git a/docs/assets/images/upload_NIRS_example_format.png b/docs/assets/images/upload_NIRS_example_format.png deleted file mode 100644 index 282783a3ee..0000000000 Binary files a/docs/assets/images/upload_NIRS_example_format.png and /dev/null differ diff --git a/docs/assets/images/upload_NIRS_plot_example.png b/docs/assets/images/upload_NIRS_plot_example.png deleted file mode 100644 index 882f90c2ca..0000000000 Binary files a/docs/assets/images/upload_NIRS_plot_example.png and /dev/null differ diff --git a/docs/assets/images/upload_crosses.png b/docs/assets/images/upload_crosses.png deleted file mode 100644 index 3554bc3026..0000000000 Binary files a/docs/assets/images/upload_crosses.png and /dev/null differ diff --git a/docs/assets/images/vectorButtons.png b/docs/assets/images/vectorButtons.png deleted file mode 100644 index e047305a31..0000000000 Binary files a/docs/assets/images/vectorButtons.png and /dev/null differ diff --git a/docs/assets/images/vectorHighlight.png b/docs/assets/images/vectorHighlight.png deleted file mode 100644 index dc858c617e..0000000000 Binary files a/docs/assets/images/vectorHighlight.png and /dev/null differ diff --git a/docs/assets/images/vectorViewer.png b/docs/assets/images/vectorViewer.png deleted file mode 100644 index 6e47081717..0000000000 Binary files a/docs/assets/images/vectorViewer.png and /dev/null differ diff --git a/docs/assets/images/waves_breedbase_schema.png b/docs/assets/images/waves_breedbase_schema.png deleted file mode 100644 index 35c79f61e6..0000000000 Binary files a/docs/assets/images/waves_breedbase_schema.png and /dev/null differ diff --git a/docs/assets/images/wizard_any_min_all_toggle.jpg b/docs/assets/images/wizard_any_min_all_toggle.jpg deleted file mode 100644 index b93f2baafe..0000000000 Binary files a/docs/assets/images/wizard_any_min_all_toggle.jpg and /dev/null differ diff --git a/docs/assets/images/wizard_any_min_all_toggle_min_details.jpg b/docs/assets/images/wizard_any_min_all_toggle_min_details.jpg deleted file mode 100644 index 5445ccf87f..0000000000 Binary files a/docs/assets/images/wizard_any_min_all_toggle_min_details.jpg and /dev/null differ diff --git a/docs/assets/images/wizard_download_options.png b/docs/assets/images/wizard_download_options.png deleted file mode 100644 index 89b9ff12e3..0000000000 Binary files a/docs/assets/images/wizard_download_options.png and /dev/null differ diff --git a/docs/assets/images/wizard_interface.png b/docs/assets/images/wizard_interface.png deleted file mode 100644 index 5d21c76547..0000000000 Binary files a/docs/assets/images/wizard_interface.png and /dev/null differ diff --git a/docs/assets/images/wizard_interface_selections.png b/docs/assets/images/wizard_interface_selections.png deleted file mode 100644 index 89441ce986..0000000000 Binary files a/docs/assets/images/wizard_interface_selections.png and /dev/null differ diff --git a/docs/assets/images/wizard_load_create_dataset.png b/docs/assets/images/wizard_load_create_dataset.png deleted file mode 100644 index 156ac0d2dc..0000000000 Binary files a/docs/assets/images/wizard_load_create_dataset.png and /dev/null differ diff --git a/docs/assets/images/wizard_metadata_download.png b/docs/assets/images/wizard_metadata_download.png deleted file mode 100644 index e7796e0120..0000000000 Binary files a/docs/assets/images/wizard_metadata_download.png and /dev/null differ diff --git a/docs/assets/images/wizard_related_metadata_download.png b/docs/assets/images/wizard_related_metadata_download.png deleted file mode 100644 index f286748635..0000000000 Binary files a/docs/assets/images/wizard_related_metadata_download.png and /dev/null differ diff --git a/docs/assets/images/wizard_related_phenotypes_download.png b/docs/assets/images/wizard_related_phenotypes_download.png deleted file mode 100644 index c2ca9c3314..0000000000 Binary files a/docs/assets/images/wizard_related_phenotypes_download.png and /dev/null differ diff --git a/docs/assets/images/wizard_save_current_selection.png b/docs/assets/images/wizard_save_current_selection.png deleted file mode 100644 index 85639582d5..0000000000 Binary files a/docs/assets/images/wizard_save_current_selection.png and /dev/null differ diff --git a/docs/assets/images/wizard_select_list.png b/docs/assets/images/wizard_select_list.png deleted file mode 100644 index 368ed0b7ca..0000000000 Binary files a/docs/assets/images/wizard_select_list.png and /dev/null differ diff --git a/docs/assets/images/wizard_update_list_refresh.png b/docs/assets/images/wizard_update_list_refresh.png deleted file mode 100644 index 3ce5b3dd84..0000000000 Binary files a/docs/assets/images/wizard_update_list_refresh.png and /dev/null differ diff --git a/docs/assets/style.css b/docs/assets/style.css deleted file mode 100644 index 046f3253f8..0000000000 --- a/docs/assets/style.css +++ /dev/null @@ -1,15 +0,0 @@ -/*--- LOGO ---*/ - -.toc-logo { - width: 200px !important; - object-fit: contain; - margin: 0 auto; -} - -.toc-logo img { - max-width: 100%; -} - -.tab { - margin-left: 40px; -} \ No newline at end of file diff --git a/docs/assets/tipuesearch/MIT-LICENSE.txt b/docs/assets/tipuesearch/MIT-LICENSE.txt deleted file mode 100644 index 2e6ba5f92b..0000000000 --- a/docs/assets/tipuesearch/MIT-LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -Tipue Search Copyright (c) 2017 Tipue - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/assets/tipuesearch/css/tipuesearch.css b/docs/assets/tipuesearch/css/tipuesearch.css deleted file mode 100755 index 6c0b0c9e88..0000000000 --- a/docs/assets/tipuesearch/css/tipuesearch.css +++ /dev/null @@ -1,251 +0,0 @@ - -/* -Tipue Search 6.1 -Copyright (c) 2017 Tipue -Tipue Search is released under the MIT License -http://www.tipue.com/search -*/ - - -/* fonts */ - - -#tipue_search_input, #tipue_search_foot_boxes -{ - font: 300 14px/1 Roboto, sans-serif; -} -#tipue_search_results_count, #tipue_search_warning, .tipue_search_content_url, .tipue_search_content_debug, .tipue_search_related_text -{ - font: 300 14px/1.7 Roboto, sans-serif; -} -.tipue_search_content_title -{ - font: 100 26px/1.7 Roboto, sans-serif; -} -.tipue_search_content_text, .tipue_search_related_title -{ - font: 300 15px/1.7 Roboto, sans-serif; -} -.tipue_search_content_bold, .tipue_search_related_bold -{ - font-weight: 400; -} - - -/* search box */ - - -#tipue_search_input -{ - color: #333; - max-width: 210px; - padding: 17px; - border: 1px solid #e3e3e3; - border-radius: 0; - -moz-appearance: none; - -webkit-appearance: none; - box-shadow: none; - outline: 0; - margin: 0; -} -.tipue_search_icon -{ - width: 24px; - height: 24px; -} -.tipue_search_left -{ - float: left; - padding: 15px 9px 0 0; -} -.tipue_search_right -{ - float: left; -} - - -/* search results */ - - -#tipue_search_content -{ - max-width: 750px; - padding-top: 15px; - margin: 0; -} -#tipue_search_results_count -{ - color: #333; -} -#tipue_search_warning -{ - color: #333; - margin: 7px 0; -} -#tipue_search_warning a -{ - color: #5396ea; - text-decoration: none; -} -#tipue_search_warning a:hover -{ - color: #555; -} -.tipue_search_content_title -{ - color: #666; - margin-top: 21px; -} -.tipue_search_content_title a -{ - color: #666; - text-decoration: none; -} -.tipue_search_content_title a:hover -{ - color: #666; -} -.tipue_search_content_url -{ - word-wrap: break-word; - hyphens: auto; -} -.tipue_search_content_url a, .tipue_search_related_text a -{ - color: #5396ea; - text-decoration: none; -} -.tipue_search_content_url a:hover, .tipue_search_related_text a:hover, .tipue_search_related_before, .tipue_search_related_after -{ - color: #555; -} -.tipue_search_content_text -{ - color: #333; - word-wrap: break-word; - hyphens: auto; - margin-top: 5px; -} -.tipue_search_content_bold -{ - color: #333; -} -.tipue_search_content_debug -{ - color: #333; - margin: 5px 0; -} -.tipue_search_related_title -{ - color: #333; - margin: 26px 0 7px 0; -} -.tipue_search_related_cols -{ - -webkit-columns: 230px 2; - -moz-columns: 230px 2; - columns: 230px 2; -} - -#tipue_search_foot -{ - margin: 51px 0 21px 0; -} -#tipue_search_foot_boxes -{ - padding: 0; - margin: 0; - cursor: pointer; -} -#tipue_search_foot_boxes li -{ - list-style: none; - margin: 0; - padding: 0; - display: inline; -} -#tipue_search_foot_boxes li a -{ - padding: 10px 17px 11px 17px; - background-color: #fff; - border: 1px solid #e3e3e3; - border-radius: 1px; - color: #333; - margin-right: 7px; - text-decoration: none; - text-align: center; -} -#tipue_search_foot_boxes li.current -{ - padding: 10px 17px 11px 17px; - background: #f6f6f6; - border: 1px solid #e3e3e3; - border-radius: 1px; - color: #333; - margin-right: 7px; - text-align: center; -} -#tipue_search_foot_boxes li a:hover -{ - background: #f6f6f6; -} - - -/* spinner */ - - -.tipue_search_spinner -{ - width: 50px; - height: 28px; -} -.tipue_search_spinner > div -{ - background-color: #e3e3e3; - height: 100%; - width: 2px; - display: inline-block; - margin-right: 2px; - -webkit-animation: stretchdelay 1.2s infinite ease-in-out; - animation: stretchdelay 1.2s infinite ease-in-out; -} -.tipue_search_spinner .tipue_search_rect2 -{ - -webkit-animation-delay: -1.1s; - animation-delay: -1.1s; -} -.tipue_search_spinner .tipue_search_rect3 -{ - -webkit-animation-delay: -1.0s; - animation-delay: -1.0s; -} -@-webkit-keyframes stretchdelay -{ - 0%, 40%, 100% - { - -webkit-transform: scaleY(0.4) - } - 20% - { - -webkit-transform: scaleY(1.0) - } -} -@keyframes stretchdelay -{ - 0%, 40%, 100% - { - transform: scaleY(0.4); - -webkit-transform: scaleY(0.4); - } - 20% - { - transform: scaleY(1.0); - -webkit-transform: scaleY(1.0); - } -} - - - - - - diff --git a/docs/assets/tipuesearch/tipuesearch.min.js b/docs/assets/tipuesearch/tipuesearch.min.js deleted file mode 100644 index e6e80aa162..0000000000 --- a/docs/assets/tipuesearch/tipuesearch.min.js +++ /dev/null @@ -1,178 +0,0 @@ -(function($){$.fn.tipuesearch=function(options){var set=$.extend({'contentLocation':'tipuesearch/tipuesearch_content.json','contextBuffer':60,'contextLength':60,'contextStart':90,'debug':false,'descriptiveWords':25,'highlightTerms':true,'liveContent':'*','liveDescription':'*','minimumLength':3,'mode':'static','newWindow':false,'show':9,'showContext':true,'showRelated':true,'showTime':true,'showTitleCount':true,'showURL':true,'wholeWords':true},options);return this.each(function(){var tipuesearch_in={pages:[]};$.ajaxSetup({async:false});var tipuesearch_t_c=0;$('#tipue_search_content').hide().html('
').show();if(set.mode=='live') -{for(var i=0;i');var t_2=html.toLowerCase().indexOf('',t_1+7);if(t_1!=-1&&t_2!=-1) -{var tit=html.slice(t_1+7,t_2);} -else -{var tit=tipuesearch_string_1;} -tipuesearch_in.pages.push({"title":tit,"text":desc,"tags":cont,"url":tipuesearch_pages[i]});});}} -if(set.mode=='json') -{$.getJSON(set.contentLocation).done(function(json) -{tipuesearch_in=$.extend({},json);});} -if(set.mode=='static') -{tipuesearch_in=$.extend({},tipuesearch);} -var tipue_search_w='';if(set.newWindow) -{tipue_search_w=' target="_blank"';} -function getURLP(name) -{var _locSearch=location.search;var _splitted=(new RegExp('[?|&]'+name+'='+'([^&;]+?)(&|#|;|$)').exec(_locSearch)||[,""]);var searchString=_splitted[1].replace(/\+/g,'%20');try -{searchString=decodeURIComponent(searchString);} -catch(e) -{searchString=unescape(searchString);} -return searchString||null;} -if(getURLP('q')) -{$('#tipue_search_input').val(getURLP('q'));getTipueSearch(0,true);} -$(this).keyup(function(event) -{if(event.keyCode=='13') -{getTipueSearch(0,true);}});function getTipueSearch(start,replace) -{var out='';var show_replace=false;var show_stop=false;var standard=true;var c=0;found=[];var d_o=$('#tipue_search_input').val();var d=d_o.toLowerCase();d=$.trim(d);if((d.match("^\"")&&d.match("\"$"))||(d.match("^'")&&d.match("'$"))) -{standard=false;} -var d_w=d.split(' ');if(standard) -{d='';for(var i=0;i=set.minimumLength) -{if(standard) -{if(replace) -{var d_r=d;for(var i=0;i'+d_r+'';} -if(c==1) -{out+='
'+tipuesearch_string_4;} -else -{c_c=c.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",");out+='
'+c_c+' '+tipuesearch_string_5;} -if(set.showTime) -{var endTimer=new Date().getTime();var time=(endTimer-startTimer)/ 1000;out+=' ('+time.toFixed(2)+' '+tipuesearch_string_14+')';set.showTime=false;} -out+='
';found.sort(function(a,b){return b.score-a.score});var l_o=0;for(var i=0;i=start&&l_o'+found[i].title+'
';if(set.debug) -{out+='
Score: '+found[i].score+'
';} -if(set.showURL) -{var s_u=found[i].url.toLowerCase();if(s_u.indexOf('http://')==0) -{s_u=s_u.slice(7);} -out+='';} -if(found[i].desc) -{var t=found[i].desc;if(set.showContext) -{d_w=d.split(' ');var s_1=found[i].desc.toLowerCase().indexOf(d_w[0]);if(s_1>set.contextStart) -{var t_1=t.substr(s_1-set.contextBuffer);var s_2=t_1.indexOf(' ');t_1=t.substr(s_1-set.contextBuffer+s_2);t_1=$.trim(t_1);if(t_1.length>set.contextLength) -{t='... '+t_1;}}} -if(standard) -{d_w=d.split(' ');for(var f=0;f$1");}}} -else if(set.highlightTerms) -{var patr=new RegExp('('+d+')','gi');t=t.replace(patr,"$1");} -var t_d='';var t_w=t.split(' ');if(t_w.length'+t_d+'';}} -l_o++;} -if(set.showRelated&&standard) -{f=0;for(var i=0;i'+d_o+'';}} -if(c>set.show) -{var pages=Math.ceil(c / set.show);var page=(start / set.show);out+='';}} -else -{out+='
'+tipuesearch_string_8+'
';}} -else -{if(show_stop) -{out+='
'+tipuesearch_string_8+'. '+tipuesearch_string_9+'
';} -else -{out+='
'+tipuesearch_string_10+'
';if(set.minimumLength==1) -{out+='
'+tipuesearch_string_11+'
';} -else -{out+='
'+tipuesearch_string_12+' '+set.minimumLength+' '+tipuesearch_string_13+'
';}}} -$('#tipue_search_content').hide().html(out).slideDown(200);$('#tipue_search_replaced').click(function() -{getTipueSearch(0,false);});$('.tipue_search_related').click(function() -{$('#tipue_search_input').val($(this).attr('id'));getTipueSearch(0,true);});$('.tipue_search_foot_box').click(function() -{var id_v=$(this).attr('id');var id_a=id_v.split('_');getTipueSearch(parseInt(id_a[0]),id_a[1]);});}});};})(jQuery); \ No newline at end of file diff --git a/docs/assets/tipuesearch/tipuesearch_set.js b/docs/assets/tipuesearch/tipuesearch_set.js deleted file mode 100644 index fa90ad3736..0000000000 --- a/docs/assets/tipuesearch/tipuesearch_set.js +++ /dev/null @@ -1,80 +0,0 @@ - -/* -Tipue Search 6.1 -Copyright (c) 2017 Tipue -Tipue Search is released under the MIT License -http://www.tipue.com/search -*/ - - -/* -Stop words -Stop words list from http://www.ranks.nl/stopwords -*/ - -var tipuesearch_stop_words = ["a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "aren't", "as", "at", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can't", "cannot", "could", "couldn't", "did", "didn't", "do", "does", "doesn't", "doing", "don't", "down", "during", "each", "few", "for", "from", "further", "had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "he'd", "he'll", "he's", "her", "here", "here's", "hers", "herself", "him", "himself", "his", "how", "how's", "i", "i'd", "i'll", "i'm", "i've", "if", "in", "into", "is", "isn't", "it", "it's", "its", "itself", "let's", "me", "more", "most", "mustn't", "my", "myself", "no", "nor", "not", "of", "off", "on", "once", "only", "or", "other", "ought", "our", "ours", "ourselves", "out", "over", "own", "same", "shan't", "she", "she'd", "she'll", "she's", "should", "shouldn't", "so", "some", "such", "than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", "this", "those", "through", "to", "too", "under", "until", "up", "very", "was", "wasn't", "we", "we'd", "we'll", "we're", "we've", "were", "weren't", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "who's", "whom", "why", "why's", "with", "won't", "would", "wouldn't", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself", "yourselves"]; - - -// Word replace - -var tipuesearch_replace = {'words': [ - {'word': 'tip', 'replace_with': 'tipue'}, - {'word': 'javscript', 'replace_with': 'javascript'}, - {'word': 'jqeury', 'replace_with': 'jquery'} -]}; - - -// Weighting - -var tipuesearch_weight = {'weight': [ - {'url': 'http://www.tipue.com', 'score': 20}, - {'url': 'http://www.tipue.com/search', 'score': 30}, - {'url': 'http://www.tipue.com/is', 'score': 10} -]}; - - -// Illogical stemming - -var tipuesearch_stem = {'words': [ - {'word': 'e-mail', 'stem': 'email'}, - {'word': 'javascript', 'stem': 'jquery'}, - {'word': 'javascript', 'stem': 'js'} -]}; - - -// Related searches - -var tipuesearch_related = {'searches': [ - {'search': 'tipue', 'related': 'Tipue Search'}, - {'search': 'tipue', 'before': 'Tipue Search', 'related': 'Getting Started'}, - {'search': 'tipue', 'before': 'Tipue', 'related': 'jQuery'}, - {'search': 'tipue', 'before': 'Tipue', 'related': 'Blog'} -]}; - - -// Internal strings - -var tipuesearch_string_1 = 'No title'; -var tipuesearch_string_2 = 'Showing results for'; -var tipuesearch_string_3 = 'Search instead for'; -var tipuesearch_string_4 = '1 result'; -var tipuesearch_string_5 = 'results'; -var tipuesearch_string_6 = 'Back'; -var tipuesearch_string_7 = 'More'; -var tipuesearch_string_8 = 'Nothing found.'; -var tipuesearch_string_9 = 'Common words are largely ignored.'; -var tipuesearch_string_10 = 'Search too short'; -var tipuesearch_string_11 = 'Should be one character or more.'; -var tipuesearch_string_12 = 'Should be'; -var tipuesearch_string_13 = 'characters or more.'; -var tipuesearch_string_14 = 'seconds'; -var tipuesearch_string_15 = 'Searches related to'; - - -// Internals - - -// Timer for showTime - -var startTimer = new Date().getTime(); - diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index f107bf4965..0000000000 --- a/docs/index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Breedbase & SGN Documentation" -layout: doc_folder ---- diff --git a/docs/libs/anchor-sections-1.1.0/anchor-sections-hash.css b/docs/libs/anchor-sections-1.1.0/anchor-sections-hash.css deleted file mode 100644 index b563ec97ed..0000000000 --- a/docs/libs/anchor-sections-1.1.0/anchor-sections-hash.css +++ /dev/null @@ -1,2 +0,0 @@ -/* Styles for section anchors */ -a.anchor-section::before {content: '#';font-size: 80%;} diff --git a/docs/libs/anchor-sections-1.1.0/anchor-sections.css b/docs/libs/anchor-sections-1.1.0/anchor-sections.css deleted file mode 100644 index 041905f8b5..0000000000 --- a/docs/libs/anchor-sections-1.1.0/anchor-sections.css +++ /dev/null @@ -1,4 +0,0 @@ -/* Styles for section anchors */ -a.anchor-section {margin-left: 10px; visibility: hidden; color: inherit;} -.hasAnchor:hover a.anchor-section {visibility: visible;} -ul > li > .anchor-section {display: none;} diff --git a/docs/libs/anchor-sections-1.1.0/anchor-sections.js b/docs/libs/anchor-sections-1.1.0/anchor-sections.js deleted file mode 100644 index fee005d95b..0000000000 --- a/docs/libs/anchor-sections-1.1.0/anchor-sections.js +++ /dev/null @@ -1,11 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - // If section divs is used, we need to put the anchor in the child header - const headers = document.querySelectorAll("div.hasAnchor.section[class*='level'] > :first-child") - - headers.forEach(function (x) { - // Add to the header node - if (!x.classList.contains('hasAnchor')) x.classList.add('hasAnchor') - // Remove from the section or div created by Pandoc - x.parentElement.classList.remove('hasAnchor') - }) -}) diff --git a/docs/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf b/docs/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2fa1..0000000000 Binary files a/docs/libs/gitbook-2.6.7/css/fontawesome/fontawesome-webfont.ttf and /dev/null differ diff --git a/docs/libs/gitbook-2.6.7/css/plugin-bookdown.css b/docs/libs/gitbook-2.6.7/css/plugin-bookdown.css deleted file mode 100644 index ab7c20eb34..0000000000 --- a/docs/libs/gitbook-2.6.7/css/plugin-bookdown.css +++ /dev/null @@ -1,105 +0,0 @@ -.book .book-header h1 { - padding-left: 20px; - padding-right: 20px; -} -.book .book-header.fixed { - position: fixed; - right: 0; - top: 0; - left: 0; - border-bottom: 1px solid rgba(0,0,0,.07); -} -span.search-highlight { - background-color: #ffff88; -} -@media (min-width: 600px) { - .book.with-summary .book-header.fixed { - left: 300px; - } -} -@media (max-width: 1240px) { - .book .book-body.fixed { - top: 50px; - } - .book .book-body.fixed .body-inner { - top: auto; - } -} -@media (max-width: 600px) { - .book.with-summary .book-header.fixed { - left: calc(100% - 60px); - min-width: 300px; - } - .book.with-summary .book-body { - transform: none; - left: calc(100% - 60px); - min-width: 300px; - } - .book .book-body.fixed { - top: 0; - } -} - -.book .book-body.fixed .body-inner { - top: 50px; -} -.book .book-body .page-wrapper .page-inner section.normal sub, .book .book-body .page-wrapper .page-inner section.normal sup { - font-size: 85%; -} - -@media print { - .book .book-summary, .book .book-body .book-header, .fa { - display: none !important; - } - .book .book-body.fixed { - left: 0px; - } - .book .book-body,.book .book-body .body-inner, .book.with-summary { - overflow: visible !important; - } -} -.kable_wrapper { - border-spacing: 20px 0; - border-collapse: separate; - border: none; - margin: auto; -} -.kable_wrapper > tbody > tr > td { - vertical-align: top; -} -.book .book-body .page-wrapper .page-inner section.normal table tr.header { - border-top-width: 2px; -} -.book .book-body .page-wrapper .page-inner section.normal table tr:last-child td { - border-bottom-width: 2px; -} -.book .book-body .page-wrapper .page-inner section.normal table td, .book .book-body .page-wrapper .page-inner section.normal table th { - border-left: none; - border-right: none; -} -.book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr, .book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr > td { - border-top: none; -} -.book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr:last-child > td { - border-bottom: none; -} - -div.theorem, div.lemma, div.corollary, div.proposition, div.conjecture { - font-style: italic; -} -span.theorem, span.lemma, span.corollary, span.proposition, span.conjecture { - font-style: normal; -} -div.proof>*:last-child:after { - content: "\25a2"; - float: right; -} -.header-section-number { - padding-right: .5em; -} -#header .multi-author { - margin: 0.5em 0 -0.5em 0; -} -#header .date { - margin-top: 1.5em; -} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-clipboard.css b/docs/libs/gitbook-2.6.7/css/plugin-clipboard.css deleted file mode 100644 index 6844a70aa5..0000000000 --- a/docs/libs/gitbook-2.6.7/css/plugin-clipboard.css +++ /dev/null @@ -1,18 +0,0 @@ -div.sourceCode { - position: relative; -} - -.copy-to-clipboard-button { - position: absolute; - right: 0; - top: 0; - visibility: hidden; -} - -.copy-to-clipboard-button:focus { - outline: 0; -} - -div.sourceCode:hover > .copy-to-clipboard-button { - visibility: visible; -} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-fontsettings.css b/docs/libs/gitbook-2.6.7/css/plugin-fontsettings.css deleted file mode 100644 index 3fa6f35b2a..0000000000 --- a/docs/libs/gitbook-2.6.7/css/plugin-fontsettings.css +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Theme 1 - */ -.color-theme-1 .dropdown-menu { - background-color: #111111; - border-color: #7e888b; -} -.color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { - border-bottom: 9px solid #111111; -} -.color-theme-1 .dropdown-menu .buttons { - border-color: #7e888b; -} -.color-theme-1 .dropdown-menu .button { - color: #afa790; -} -.color-theme-1 .dropdown-menu .button:hover { - color: #73553c; -} -/* - * Theme 2 - */ -.color-theme-2 .dropdown-menu { - background-color: #2d3143; - border-color: #272a3a; -} -.color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { - border-bottom: 9px solid #2d3143; -} -.color-theme-2 .dropdown-menu .buttons { - border-color: #272a3a; -} -.color-theme-2 .dropdown-menu .button { - color: #62677f; -} -.color-theme-2 .dropdown-menu .button:hover { - color: #f4f4f5; -} -.book .book-header .font-settings .font-enlarge { - line-height: 30px; - font-size: 1.4em; -} -.book .book-header .font-settings .font-reduce { - line-height: 30px; - font-size: 1em; -} - -/* sidebar transition background */ -div.book.color-theme-1 { - background: #f3eacb; -} -.book.color-theme-1 .book-body { - color: #704214; - background: #f3eacb; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section { - background: #f3eacb; -} - -/* sidebar transition background */ -div.book.color-theme-2 { - background: #1c1f2b; -} - -.book.color-theme-2 .book-body { - color: #bdcadb; - background: #1c1f2b; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section { - background: #1c1f2b; -} -.book.font-size-0 .book-body .page-inner section { - font-size: 1.2rem; -} -.book.font-size-1 .book-body .page-inner section { - font-size: 1.4rem; -} -.book.font-size-2 .book-body .page-inner section { - font-size: 1.6rem; -} -.book.font-size-3 .book-body .page-inner section { - font-size: 2.2rem; -} -.book.font-size-4 .book-body .page-inner section { - font-size: 4rem; -} -.book.font-family-0 { - font-family: Georgia, serif; -} -.book.font-family-1 { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { - color: #704214; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { - color: inherit; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { - color: inherit; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { - border-color: inherit; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { - color: inherit; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { - background-color: inherit; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { - border-color: #c4b29f; - opacity: 0.9; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { - background: #fdf6e3; - color: #657b83; - border-color: #f8df9c; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { - background-color: inherit; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { - border-color: #f5d06c; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { - color: inherit; - background-color: #fdf6e3; - border-color: #444444; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { - background-color: #fbeecb; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { - color: #bdcadb; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { - color: #3eb1d0; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { - color: #fffffa; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { - border-color: #373b4e; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { - color: #373b4e; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { - background-color: #373b4e; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { - border-color: #373b4e; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { - color: #9dbed8; - background: #2d3143; - border-color: #2d3143; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { - background-color: #282a39; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { - border-color: #3b3f54; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { - color: #b6c2d2; - background-color: #2d3143; - border-color: #3b3f54; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { - background-color: #35394b; -} -.book.color-theme-1 .book-header { - color: #afa790; - background: transparent; -} -.book.color-theme-1 .book-header .btn { - color: #afa790; -} -.book.color-theme-1 .book-header .btn:hover { - color: #73553c; - background: none; -} -.book.color-theme-1 .book-header h1 { - color: #704214; -} -.book.color-theme-2 .book-header { - color: #7e888b; - background: transparent; -} -.book.color-theme-2 .book-header .btn { - color: #3b3f54; -} -.book.color-theme-2 .book-header .btn:hover { - color: #fffff5; - background: none; -} -.book.color-theme-2 .book-header h1 { - color: #bdcadb; -} -.book.color-theme-1 .book-body .navigation { - color: #afa790; -} -.book.color-theme-1 .book-body .navigation:hover { - color: #73553c; -} -.book.color-theme-2 .book-body .navigation { - color: #383f52; -} -.book.color-theme-2 .book-body .navigation:hover { - color: #fffff5; -} -/* - * Theme 1 - */ -.book.color-theme-1 .book-summary { - color: #afa790; - background: #111111; - border-right: 1px solid rgba(0, 0, 0, 0.07); -} -.book.color-theme-1 .book-summary .book-search { - background: transparent; -} -.book.color-theme-1 .book-summary .book-search input, -.book.color-theme-1 .book-summary .book-search input:focus { - border: 1px solid transparent; -} -.book.color-theme-1 .book-summary ul.summary li.divider { - background: #7e888b; - box-shadow: none; -} -.book.color-theme-1 .book-summary ul.summary li i.fa-check { - color: #33cc33; -} -.book.color-theme-1 .book-summary ul.summary li.done > a { - color: #877f6a; -} -.book.color-theme-1 .book-summary ul.summary li a, -.book.color-theme-1 .book-summary ul.summary li span { - color: #877f6a; - background: transparent; - font-weight: normal; -} -.book.color-theme-1 .book-summary ul.summary li.active > a, -.book.color-theme-1 .book-summary ul.summary li a:hover { - color: #704214; - background: transparent; - font-weight: normal; -} -/* - * Theme 2 - */ -.book.color-theme-2 .book-summary { - color: #bcc1d2; - background: #2d3143; - border-right: none; -} -.book.color-theme-2 .book-summary .book-search { - background: transparent; -} -.book.color-theme-2 .book-summary .book-search input, -.book.color-theme-2 .book-summary .book-search input:focus { - border: 1px solid transparent; -} -.book.color-theme-2 .book-summary ul.summary li.divider { - background: #272a3a; - box-shadow: none; -} -.book.color-theme-2 .book-summary ul.summary li i.fa-check { - color: #33cc33; -} -.book.color-theme-2 .book-summary ul.summary li.done > a { - color: #62687f; -} -.book.color-theme-2 .book-summary ul.summary li a, -.book.color-theme-2 .book-summary ul.summary li span { - color: #c1c6d7; - background: transparent; - font-weight: 600; -} -.book.color-theme-2 .book-summary ul.summary li.active > a, -.book.color-theme-2 .book-summary ul.summary li a:hover { - color: #f4f4f5; - background: #252737; - font-weight: 600; -} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-highlight.css b/docs/libs/gitbook-2.6.7/css/plugin-highlight.css deleted file mode 100644 index 2aabd3debc..0000000000 --- a/docs/libs/gitbook-2.6.7/css/plugin-highlight.css +++ /dev/null @@ -1,426 +0,0 @@ -.book .book-body .page-wrapper .page-inner section.normal pre, -.book .book-body .page-wrapper .page-inner section.normal code { - /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ - /* Tomorrow Comment */ - /* Tomorrow Red */ - /* Tomorrow Orange */ - /* Tomorrow Yellow */ - /* Tomorrow Green */ - /* Tomorrow Aqua */ - /* Tomorrow Blue */ - /* Tomorrow Purple */ -} -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-comment, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-title { - color: #8e908c; -} -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-variable, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-tag, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-tag, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, -.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant, -.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant, -.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi, -.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi, -.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype, -.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype, -.book .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype, -.book .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype, -.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id, -.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-id, -.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class, -.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-class, -.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, -.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo { - color: #c82829; -} -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-number, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-number, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-literal, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-literal, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-params, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-params, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-constant { - color: #f5871f; -} -.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute, -.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute { - color: #eab700; -} -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-string, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-string, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-value, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-value, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance, -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-header, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-header, -.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol, -.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol, -.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, -.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { - color: #718c00; -} -.book .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor, -.book .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor { - color: #3e999f; -} -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-function, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-function, -.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator, -.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator, -.book .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal code .python .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword, -.book .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword, -.book .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub, -.book .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub, -.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title, -.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title { - color: #4271ae; -} -.book .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, -.book .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, -.book .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function, -.book .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function { - color: #8959a8; -} -.book .book-body .page-wrapper .page-inner section.normal pre .hljs, -.book .book-body .page-wrapper .page-inner section.normal code .hljs { - display: block; - background: white; - color: #4d4d4c; - padding: 0.5em; -} -.book .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript, -.book .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript, -.book .book-body .page-wrapper .page-inner section.normal pre .javascript .xml, -.book .book-body .page-wrapper .page-inner section.normal code .javascript .xml, -.book .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, -.book .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, -.book .book-body .page-wrapper .page-inner section.normal pre .xml .javascript, -.book .book-body .page-wrapper .page-inner section.normal code .xml .javascript, -.book .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript, -.book .book-body .page-wrapper .page-inner section.normal code .xml .vbscript, -.book .book-body .page-wrapper .page-inner section.normal pre .xml .css, -.book .book-body .page-wrapper .page-inner section.normal code .xml .css, -.book .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, -.book .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { - opacity: 0.5; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { - /* - -Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull - -*/ - /* Solarized Green */ - /* Solarized Cyan */ - /* Solarized Blue */ - /* Solarized Yellow */ - /* Solarized Orange */ - /* Solarized Red */ - /* Solarized Violet */ -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs { - display: block; - padding: 0.5em; - background: #fdf6e3; - color: #657b83; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-comment, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-template_comment, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-template_comment, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-header, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-header, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-doctype, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-doctype, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pi, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pi, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-string, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-string, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-javadoc, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-javadoc { - color: #93a1a1; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-winutils, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-winutils, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .method, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .method, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-addition, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-addition, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-tag, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-tag, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-request, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-request, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-status, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-status, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .nginx .hljs-title, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .nginx .hljs-title { - color: #859900; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-number, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-number, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-command, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-command, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-string, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-string, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag .hljs-value, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-tag .hljs-value, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-rules .hljs-value, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-rules .hljs-value, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-phpdoc, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-phpdoc, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-hexcolor, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-hexcolor, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_url, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_url { - color: #2aa198; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-title, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-title, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-localvars, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-localvars, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-chunk, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-chunk, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-decorator, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-decorator, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-identifier, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-identifier, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .vhdl .hljs-literal, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .vhdl .hljs-literal, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-id, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-id, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-function, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-function { - color: #268bd2; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-variable, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .lisp .hljs-body, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .lisp .hljs-body, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .smalltalk .hljs-number, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .smalltalk .hljs-number, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-constant, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-class .hljs-title, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-class .hljs-title, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-parent, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-parent, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .haskell .hljs-type, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .haskell .hljs-type, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_reference, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_reference { - color: #b58900; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor .hljs-keyword, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor .hljs-keyword, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-shebang, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-shebang, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-symbol .hljs-string, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-symbol .hljs-string, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .diff .hljs-change, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .diff .hljs-change, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-special, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-special, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-attr_selector, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-attr_selector, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-subst, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-subst, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-cdata, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-cdata, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .clojure .hljs-title, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .clojure .hljs-title, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-header, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-header { - color: #cb4b16; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-deletion, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-deletion, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-important, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-important { - color: #dc322f; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .hljs-link_label, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .hljs-link_label { - color: #6c71c4; -} -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, -.book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula { - background: #eee8d5; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { - /* Tomorrow Night Bright Theme */ - /* Original theme - https://github.com/chriskempson/tomorrow-theme */ - /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ - /* Tomorrow Comment */ - /* Tomorrow Red */ - /* Tomorrow Orange */ - /* Tomorrow Yellow */ - /* Tomorrow Green */ - /* Tomorrow Aqua */ - /* Tomorrow Blue */ - /* Tomorrow Purple */ -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-comment, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-comment, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-title { - color: #969896; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-variable, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-variable, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-attribute, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-attribute, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-tag, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-tag, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-regexp, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-regexp, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-constant, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-constant, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-tag .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-tag .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-pi, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-pi, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-doctype, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-doctype, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .html .hljs-doctype, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .html .hljs-doctype, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-id, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-id, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-class, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-class, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-pseudo, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-pseudo { - color: #d54e53; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-number, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-number, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-preprocessor, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-preprocessor, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-pragma, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-pragma, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-built_in, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-built_in, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-literal, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-literal, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-params, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-params, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-constant, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-constant { - color: #e78c45; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-class .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-class .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-rules .hljs-attribute, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-rules .hljs-attribute { - color: #e7c547; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-string, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-string, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-value, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-value, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-inheritance, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-inheritance, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-header, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-header, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-symbol, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-symbol, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { - color: #b9ca4a; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .css .hljs-hexcolor, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .css .hljs-hexcolor { - color: #70c0b1; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-function, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-function, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-decorator, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-decorator, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .python .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .python .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-function .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-function .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .ruby .hljs-title .hljs-keyword, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .ruby .hljs-title .hljs-keyword, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .perl .hljs-sub, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .perl .hljs-sub, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .hljs-title, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .hljs-title { - color: #7aa6da; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs-keyword, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs-keyword, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .hljs-function, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .hljs-function { - color: #c397d8; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .hljs, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .hljs { - display: block; - background: black; - color: #eaeaea; - padding: 0.5em; -} -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .coffeescript .javascript, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .coffeescript .javascript, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .javascript .xml, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .javascript .xml, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .tex .hljs-formula, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .tex .hljs-formula, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .javascript, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .javascript, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .vbscript, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .vbscript, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .css, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .css, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre .xml .hljs-cdata, -.book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code .xml .hljs-cdata { - opacity: 0.5; -} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-search.css b/docs/libs/gitbook-2.6.7/css/plugin-search.css deleted file mode 100644 index c85e557aaa..0000000000 --- a/docs/libs/gitbook-2.6.7/css/plugin-search.css +++ /dev/null @@ -1,31 +0,0 @@ -.book .book-summary .book-search { - padding: 6px; - background: transparent; - position: absolute; - top: -50px; - left: 0px; - right: 0px; - transition: top 0.5s ease; -} -.book .book-summary .book-search input, -.book .book-summary .book-search input:focus, -.book .book-summary .book-search input:hover { - width: 100%; - background: transparent; - border: 1px solid #ccc; - box-shadow: none; - outline: none; - line-height: 22px; - padding: 7px 4px; - color: inherit; - box-sizing: border-box; -} -.book.with-search .book-summary .book-search { - top: 0px; -} -.book.with-search .book-summary ul.summary { - top: 50px; -} -.with-search .summary li[data-level] a[href*=".html#"] { - display: none; -} diff --git a/docs/libs/gitbook-2.6.7/css/plugin-table.css b/docs/libs/gitbook-2.6.7/css/plugin-table.css deleted file mode 100644 index 7fba1b9fb6..0000000000 --- a/docs/libs/gitbook-2.6.7/css/plugin-table.css +++ /dev/null @@ -1 +0,0 @@ -.book .book-body .page-wrapper .page-inner section.normal table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.book .book-body .page-wrapper .page-inner section.normal table td,.book .book-body .page-wrapper .page-inner section.normal table th{padding:6px 13px;border:1px solid #ddd}.book .book-body .page-wrapper .page-inner section.normal table tr{background-color:#fff;border-top:1px solid #ccc}.book .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n){background-color:#f8f8f8}.book .book-body .page-wrapper .page-inner section.normal table th{font-weight:700} diff --git a/docs/libs/gitbook-2.6.7/css/style.css b/docs/libs/gitbook-2.6.7/css/style.css deleted file mode 100644 index cba69b23b4..0000000000 --- a/docs/libs/gitbook-2.6.7/css/style.css +++ /dev/null @@ -1,13 +0,0 @@ -/*! normalize.css v2.1.0 | MIT License | git.io/normalize */img,legend{border:0}*{-webkit-font-smoothing:antialiased}sub,sup{position:relative}.book .book-body .page-wrapper .page-inner section.normal hr:after,.book-langs-index .inner .languages:after,.buttons:after,.dropdown-menu .buttons:after{clear:both}body,html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}.hidden,[hidden]{display:none}audio:not([controls]){display:none;height:0}html{font-family:sans-serif}body,figure{margin:0}a:focus{outline:dotted thin}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}svg:not(:root){overflow:hidden}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button{margin-right:10px;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}/*! - * Preboot v2 - * - * Open sourced under MIT license by @mdo. - * Some variables and mixins from Bootstrap (Apache 2 license). - */.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('./fontawesome/fontawesome-webfont.ttf?v=4.7.0') format('truetype');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} -.book .book-header,.book .book-summary{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.book-langs-index{width:100%;height:100%;padding:40px 0;margin:0;overflow:auto}@media (max-width:600px){.book-langs-index{padding:0}}.book-langs-index .inner{max-width:600px;width:100%;margin:0 auto;padding:30px;background:#fff;border-radius:3px}.book-langs-index .inner h3{margin:0}.book-langs-index .inner .languages{list-style:none;padding:20px 30px;margin-top:20px;border-top:1px solid #eee}.book-langs-index .inner .languages:after,.book-langs-index .inner .languages:before{content:" ";display:table;line-height:0}.book-langs-index .inner .languages li{width:50%;float:left;padding:10px 5px;font-size:16px}@media (max-width:600px){.book-langs-index .inner .languages li{width:100%;max-width:100%}}.book .book-header{overflow:visible;height:50px;padding:0 8px;z-index:2;font-size:.85em;color:#7e888b;background:0 0}.book .book-header .btn{display:block;height:50px;padding:0 15px;border-bottom:none;color:#ccc;text-transform:uppercase;line-height:50px;-webkit-box-shadow:none!important;box-shadow:none!important;position:relative;font-size:14px}.book .book-header .btn:hover{position:relative;text-decoration:none;color:#444;background:0 0}.book .book-header h1{margin:0;font-size:20px;font-weight:200;text-align:center;line-height:50px;opacity:0;padding-left:200px;padding-right:200px;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;-o-transition:opacity .2s ease;transition:opacity .2s ease;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.book .book-header h1 a,.book .book-header h1 a:hover{color:inherit;text-decoration:none}@media (max-width:1000px){.book .book-header h1{display:none}}.book .book-header h1 i{display:none}.book .book-header:hover h1{opacity:1}.book.is-loading .book-header h1 i{display:inline-block}.book.is-loading .book-header h1 a{display:none}.dropdown{position:relative}.dropdown-menu{position:absolute;top:100%;left:0;z-index:100;display:none;float:left;min-width:160px;padding:0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fafafa;border:1px solid rgba(0,0,0,.07);border-radius:1px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.open{display:block}.dropdown-menu.dropdown-left{left:auto;right:4%}.dropdown-menu.dropdown-left .dropdown-caret{right:14px;left:auto}.dropdown-menu .dropdown-caret{position:absolute;top:-8px;left:14px;width:18px;height:10px;float:left;overflow:hidden}.dropdown-menu .dropdown-caret .caret-inner,.dropdown-menu .dropdown-caret .caret-outer{display:inline-block;top:0;border-left:9px solid transparent;border-right:9px solid transparent;position:absolute}.dropdown-menu .dropdown-caret .caret-outer{border-bottom:9px solid rgba(0,0,0,.1);height:auto;left:0;width:auto;margin-left:-1px}.dropdown-menu .dropdown-caret .caret-inner{margin-top:-1px;top:1px;border-bottom:9px solid #fafafa}.dropdown-menu .buttons{border-bottom:1px solid rgba(0,0,0,.07)}.dropdown-menu .buttons:after,.dropdown-menu .buttons:before{content:" ";display:table;line-height:0}.dropdown-menu .buttons:last-child{border-bottom:none}.dropdown-menu .buttons .button{border:0;background-color:transparent;color:#a6a6a6;width:100%;text-align:center;float:left;line-height:1.42857143;padding:8px 4px}.alert,.dropdown-menu .buttons .button:hover{color:#444}.dropdown-menu .buttons .button:focus,.dropdown-menu .buttons .button:hover{outline:0}.dropdown-menu .buttons .button.size-2{width:50%}.dropdown-menu .buttons .button.size-3{width:33%}.alert{padding:15px;margin-bottom:20px;background:#eee;border-bottom:5px solid #ddd}.alert-success{background:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-info{background:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-danger{background:#f2dede;border-color:#ebccd1;color:#a94442}.alert-warning{background:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.book .book-summary{position:absolute;top:0;left:-300px;bottom:0;z-index:1;width:300px;color:#364149;background:#fafafa;border-right:1px solid rgba(0,0,0,.07);-webkit-transition:left 250ms ease;-moz-transition:left 250ms ease;-o-transition:left 250ms ease;transition:left 250ms ease}.book .book-summary ul.summary{position:absolute;top:0;left:0;right:0;bottom:0;overflow-y:auto;list-style:none;margin:0;padding:0;-webkit-transition:top .5s ease;-moz-transition:top .5s ease;-o-transition:top .5s ease;transition:top .5s ease}.book .book-summary ul.summary li{list-style:none}.book .book-summary ul.summary li.divider{height:1px;margin:7px 0;overflow:hidden;background:rgba(0,0,0,.07)}.book .book-summary ul.summary li i.fa-check{display:none;position:absolute;right:9px;top:16px;font-size:9px;color:#3c3}.book .book-summary ul.summary li.done>a{color:#364149;font-weight:400}.book .book-summary ul.summary li.done>a i{display:inline}.book .book-summary ul.summary li a,.book .book-summary ul.summary li span{display:block;padding:10px 15px;border-bottom:none;color:#364149;background:0 0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;position:relative}.book .book-summary ul.summary li span{cursor:not-allowed;opacity:.3;filter:alpha(opacity=30)}.book .book-summary ul.summary li a:hover,.book .book-summary ul.summary li.active>a{color:#008cff;background:0 0;text-decoration:none}.book .book-summary ul.summary li ul{padding-left:20px}@media (max-width:600px){.book .book-summary{width:calc(100% - 60px);bottom:0;left:-100%}}.book.with-summary .book-summary{left:0}.book.without-animation .book-summary{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;transition:none!important}.book{position:relative;width:100%;height:100%}.book .book-body,.book .book-body .body-inner{position:absolute;top:0;left:0;overflow-y:auto;bottom:0;right:0}.book .book-body{color:#000;background:#fff;-webkit-transition:left 250ms ease;-moz-transition:left 250ms ease;-o-transition:left 250ms ease;transition:left 250ms ease}.book .book-body .page-wrapper{position:relative;outline:0}.book .book-body .page-wrapper .page-inner{max-width:800px;margin:0 auto;padding:20px 0 40px}.book .book-body .page-wrapper .page-inner section{margin:0;padding:5px 15px;background:#fff;border-radius:2px;line-height:1.7;font-size:1.6rem}.book .book-body .page-wrapper .page-inner .btn-group .btn{border-radius:0;background:#eee;border:0}@media (max-width:1240px){.book .book-body{-webkit-transition:-webkit-transform 250ms ease;-moz-transition:-moz-transform 250ms ease;-o-transition:-o-transform 250ms ease;transition:transform 250ms ease;padding-bottom:20px}.book .book-body .body-inner{position:static;min-height:calc(100% - 50px)}}@media (min-width:600px){.book.with-summary .book-body{left:300px}}@media (max-width:600px){.book.with-summary{overflow:hidden}.book.with-summary .book-body{-webkit-transform:translate(calc(100% - 60px),0);-moz-transform:translate(calc(100% - 60px),0);-ms-transform:translate(calc(100% - 60px),0);-o-transform:translate(calc(100% - 60px),0);transform:translate(calc(100% - 60px),0)}}.book.without-animation .book-body{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;transition:none!important}.buttons:after,.buttons:before{content:" ";display:table;line-height:0}.button{border:0;background:#eee;color:#666;width:100%;text-align:center;float:left;line-height:1.42857143;padding:8px 4px}.button:hover{color:#444}.button:focus,.button:hover{outline:0}.button.size-2{width:50%}.button.size-3{width:33%}.book .book-body .page-wrapper .page-inner section{display:none}.book .book-body .page-wrapper .page-inner section.normal{display:block;word-wrap:break-word;overflow:hidden;color:#333;line-height:1.7;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%}.book .book-body .page-wrapper .page-inner section.normal *{box-sizing:border-box;-webkit-box-sizing:border-box;}.book .book-body .page-wrapper .page-inner section.normal>:first-child{margin-top:0!important}.book .book-body .page-wrapper .page-inner section.normal>:last-child{margin-bottom:0!important}.book .book-body .page-wrapper .page-inner section.normal blockquote,.book .book-body .page-wrapper .page-inner section.normal code,.book .book-body .page-wrapper .page-inner section.normal figure,.book .book-body .page-wrapper .page-inner section.normal img,.book .book-body .page-wrapper .page-inner section.normal pre,.book .book-body .page-wrapper .page-inner section.normal table,.book .book-body .page-wrapper .page-inner section.normal tr{page-break-inside:avoid}.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5,.book .book-body .page-wrapper .page-inner section.normal p{orphans:3;widows:3}.book .book-body .page-wrapper .page-inner section.normal h1,.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5{page-break-after:avoid}.book .book-body .page-wrapper .page-inner section.normal b,.book .book-body .page-wrapper .page-inner section.normal strong{font-weight:700}.book .book-body .page-wrapper .page-inner section.normal em{font-style:italic}.book .book-body .page-wrapper .page-inner section.normal blockquote,.book .book-body .page-wrapper .page-inner section.normal dl,.book .book-body .page-wrapper .page-inner section.normal ol,.book .book-body .page-wrapper .page-inner section.normal p,.book .book-body .page-wrapper .page-inner section.normal table,.book .book-body .page-wrapper .page-inner section.normal ul{margin-top:0;margin-bottom:.85em}.book .book-body .page-wrapper .page-inner section.normal a{color:#4183c4;text-decoration:none;background:0 0}.book .book-body .page-wrapper .page-inner section.normal a:active,.book .book-body .page-wrapper .page-inner section.normal a:focus,.book .book-body .page-wrapper .page-inner section.normal a:hover{outline:0;text-decoration:underline}.book .book-body .page-wrapper .page-inner section.normal img{border:0;max-width:100%}.book .book-body .page-wrapper .page-inner section.normal hr{height:4px;padding:0;margin:1.7em 0;overflow:hidden;background-color:#e7e7e7;border:none}.book .book-body .page-wrapper .page-inner section.normal hr:after,.book .book-body .page-wrapper .page-inner section.normal hr:before{display:table;content:" "}.book .book-body .page-wrapper .page-inner section.normal h1,.book .book-body .page-wrapper .page-inner section.normal h2,.book .book-body .page-wrapper .page-inner section.normal h3,.book .book-body .page-wrapper .page-inner section.normal h4,.book .book-body .page-wrapper .page-inner section.normal h5,.book .book-body .page-wrapper .page-inner section.normal h6{margin-top:1.275em;margin-bottom:.85em;}.book .book-body .page-wrapper .page-inner section.normal h1{font-size:2em}.book .book-body .page-wrapper .page-inner section.normal h2{font-size:1.75em}.book .book-body .page-wrapper .page-inner section.normal h3{font-size:1.5em}.book .book-body .page-wrapper .page-inner section.normal h4{font-size:1.25em}.book .book-body .page-wrapper .page-inner section.normal h5{font-size:1em}.book .book-body .page-wrapper .page-inner section.normal h6{font-size:1em;color:#777}.book .book-body .page-wrapper .page-inner section.normal code,.book .book-body .page-wrapper .page-inner section.normal pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none;color:inherit}.book .book-body .page-wrapper .page-inner section.normal pre{overflow:auto;word-wrap:normal;margin:0 0 1.275em;padding:.85em 1em;background:#f7f7f7}.book .book-body .page-wrapper .page-inner section.normal pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.book .book-body .page-wrapper .page-inner section.normal pre>code:after,.book .book-body .page-wrapper .page-inner section.normal pre>code:before{content:normal}.book .book-body .page-wrapper .page-inner section.normal code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.book .book-body .page-wrapper .page-inner section.normal code:after,.book .book-body .page-wrapper .page-inner section.normal code:before{letter-spacing:-.2em;content:"\00a0"}.book .book-body .page-wrapper .page-inner section.normal ol,.book .book-body .page-wrapper .page-inner section.normal ul{padding:0 0 0 2em;margin:0 0 .85em}.book .book-body .page-wrapper .page-inner section.normal ol ol,.book .book-body .page-wrapper .page-inner section.normal ol ul,.book .book-body .page-wrapper .page-inner section.normal ul ol,.book .book-body .page-wrapper .page-inner section.normal ul ul{margin-top:0;margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal ol ol{list-style-type:lower-roman}.book .book-body .page-wrapper .page-inner section.normal blockquote{margin:0 0 .85em;padding:0 15px;opacity:0.75;border-left:4px solid #dcdcdc}.book .book-body .page-wrapper .page-inner section.normal blockquote:first-child{margin-top:0}.book .book-body .page-wrapper .page-inner section.normal blockquote:last-child{margin-bottom:0}.book .book-body .page-wrapper .page-inner section.normal dl{padding:0}.book .book-body .page-wrapper .page-inner section.normal dl dt{padding:0;margin-top:.85em;font-style:italic;font-weight:700}.book .book-body .page-wrapper .page-inner section.normal dl dd{padding:0 .85em;margin-bottom:.85em}.book .book-body .page-wrapper .page-inner section.normal dd{margin-left:0}.book .book-body .page-wrapper .page-inner section.normal .glossary-term{cursor:help;text-decoration:underline}.book .book-body .navigation{position:absolute;top:50px;bottom:0;margin:0;max-width:150px;min-width:90px;display:flex;justify-content:center;align-content:center;flex-direction:column;font-size:40px;color:#ccc;text-align:center;-webkit-transition:all 350ms ease;-moz-transition:all 350ms ease;-o-transition:all 350ms ease;transition:all 350ms ease}.book .book-body .navigation:hover{text-decoration:none;color:#444}.book .book-body .navigation.navigation-next{right:0}.book .book-body .navigation.navigation-prev{left:0}@media (max-width:1240px){.book .book-body .navigation{position:static;top:auto;max-width:50%;width:50%;display:inline-block;float:left}.book .book-body .navigation.navigation-unique{max-width:100%;width:100%}}.book .book-body .page-wrapper .page-inner section.glossary{margin-bottom:40px}.book .book-body .page-wrapper .page-inner section.glossary h2 a,.book .book-body .page-wrapper .page-inner section.glossary h2 a:hover{color:inherit;text-decoration:none}.book .book-body .page-wrapper .page-inner section.glossary .glossary-index{list-style:none;margin:0;padding:0}.book .book-body .page-wrapper .page-inner section.glossary .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-overflow-scrolling:auto;-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:none;-webkit-touch-callout:none}a{text-decoration:none}body,html{height:100%}html{font-size:62.5%}body{text-rendering:optimizeLegibility;font-smoothing:antialiased;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;letter-spacing:.2px;text-size-adjust:100%} -.book .book-summary ul.summary li a span {display:inline;padding:initial;overflow:visible;cursor:auto;opacity:1;} -/* show arrow before summary tag as in bootstrap */ -details > summary {display:list-item;cursor:pointer;} diff --git a/docs/libs/gitbook-2.6.7/js/app.min.js b/docs/libs/gitbook-2.6.7/js/app.min.js deleted file mode 100644 index 643f1f9836..0000000000 --- a/docs/libs/gitbook-2.6.7/js/app.min.js +++ /dev/null @@ -1 +0,0 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o"'`]/g,reHasEscapedHtml=RegExp(reEscapedHtml.source),reHasUnescapedHtml=RegExp(reUnescapedHtml.source);var reEscape=/<%-([\s\S]+?)%>/g,reEvaluate=/<%([\s\S]+?)%>/g,reInterpolate=/<%=([\s\S]+?)%>/g;var reIsDeepProp=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,reIsPlainProp=/^\w*$/,rePropName=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;var reRegExpChars=/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,reHasRegExpChars=RegExp(reRegExpChars.source);var reComboMark=/[\u0300-\u036f\ufe20-\ufe23]/g;var reEscapeChar=/\\(\\)?/g;var reEsTemplate=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;var reFlags=/\w*$/;var reHasHexPrefix=/^0[xX]/;var reIsHostCtor=/^\[object .+?Constructor\]$/;var reIsUint=/^\d+$/;var reLatin1=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;var reNoMatch=/($^)/;var reUnescapedString=/['\n\r\u2028\u2029\\]/g;var reWords=function(){var upper="[A-Z\\xc0-\\xd6\\xd8-\\xde]",lower="[a-z\\xdf-\\xf6\\xf8-\\xff]+";return RegExp(upper+"+(?="+upper+lower+")|"+upper+"?"+lower+"|"+upper+"+|[0-9]+","g")}();var contextProps=["Array","ArrayBuffer","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Math","Number","Object","RegExp","Set","String","_","clearTimeout","isFinite","parseFloat","parseInt","setTimeout","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap"];var templateCounter=-1;var typedArrayTags={};typedArrayTags[float32Tag]=typedArrayTags[float64Tag]=typedArrayTags[int8Tag]=typedArrayTags[int16Tag]=typedArrayTags[int32Tag]=typedArrayTags[uint8Tag]=typedArrayTags[uint8ClampedTag]=typedArrayTags[uint16Tag]=typedArrayTags[uint32Tag]=true;typedArrayTags[argsTag]=typedArrayTags[arrayTag]=typedArrayTags[arrayBufferTag]=typedArrayTags[boolTag]=typedArrayTags[dateTag]=typedArrayTags[errorTag]=typedArrayTags[funcTag]=typedArrayTags[mapTag]=typedArrayTags[numberTag]=typedArrayTags[objectTag]=typedArrayTags[regexpTag]=typedArrayTags[setTag]=typedArrayTags[stringTag]=typedArrayTags[weakMapTag]=false;var cloneableTags={};cloneableTags[argsTag]=cloneableTags[arrayTag]=cloneableTags[arrayBufferTag]=cloneableTags[boolTag]=cloneableTags[dateTag]=cloneableTags[float32Tag]=cloneableTags[float64Tag]=cloneableTags[int8Tag]=cloneableTags[int16Tag]=cloneableTags[int32Tag]=cloneableTags[numberTag]=cloneableTags[objectTag]=cloneableTags[regexpTag]=cloneableTags[stringTag]=cloneableTags[uint8Tag]=cloneableTags[uint8ClampedTag]=cloneableTags[uint16Tag]=cloneableTags[uint32Tag]=true;cloneableTags[errorTag]=cloneableTags[funcTag]=cloneableTags[mapTag]=cloneableTags[setTag]=cloneableTags[weakMapTag]=false;var deburredLetters={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"};var htmlEscapes={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"};var htmlUnescapes={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"};var objectTypes={function:true,object:true};var regexpEscapes={0:"x30",1:"x31",2:"x32",3:"x33",4:"x34",5:"x35",6:"x36",7:"x37",8:"x38",9:"x39",A:"x41",B:"x42",C:"x43",D:"x44",E:"x45",F:"x46",a:"x61",b:"x62",c:"x63",d:"x64",e:"x65",f:"x66",n:"x6e",r:"x72",t:"x74",u:"x75",v:"x76",x:"x78"};var stringEscapes={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"};var freeExports=objectTypes[typeof exports]&&exports&&!exports.nodeType&&exports;var freeModule=objectTypes[typeof module]&&module&&!module.nodeType&&module;var freeGlobal=freeExports&&freeModule&&typeof global=="object"&&global&&global.Object&&global;var freeSelf=objectTypes[typeof self]&&self&&self.Object&&self;var freeWindow=objectTypes[typeof window]&&window&&window.Object&&window;var moduleExports=freeModule&&freeModule.exports===freeExports&&freeExports;var root=freeGlobal||freeWindow!==(this&&this.window)&&freeWindow||freeSelf||this;function baseCompareAscending(value,other){if(value!==other){var valIsNull=value===null,valIsUndef=value===undefined,valIsReflexive=value===value;var othIsNull=other===null,othIsUndef=other===undefined,othIsReflexive=other===other;if(value>other&&!othIsNull||!valIsReflexive||valIsNull&&!othIsUndef&&othIsReflexive||valIsUndef&&othIsReflexive){return 1}if(value-1){}return index}function charsRightIndex(string,chars){var index=string.length;while(index--&&chars.indexOf(string.charAt(index))>-1){}return index}function compareAscending(object,other){return baseCompareAscending(object.criteria,other.criteria)||object.index-other.index}function compareMultiple(object,other,orders){var index=-1,objCriteria=object.criteria,othCriteria=other.criteria,length=objCriteria.length,ordersLength=orders.length;while(++index=ordersLength){return result}var order=orders[index];return result*(order==="asc"||order===true?1:-1)}}return object.index-other.index}function deburrLetter(letter){return deburredLetters[letter]}function escapeHtmlChar(chr){return htmlEscapes[chr]}function escapeRegExpChar(chr,leadingChar,whitespaceChar){if(leadingChar){chr=regexpEscapes[chr]}else if(whitespaceChar){chr=stringEscapes[chr]}return"\\"+chr}function escapeStringChar(chr){return"\\"+stringEscapes[chr]}function indexOfNaN(array,fromIndex,fromRight){var length=array.length,index=fromIndex+(fromRight?0:-1);while(fromRight?index--:++index=9&&charCode<=13)||charCode==32||charCode==160||charCode==5760||charCode==6158||charCode>=8192&&(charCode<=8202||charCode==8232||charCode==8233||charCode==8239||charCode==8287||charCode==12288||charCode==65279)}function replaceHolders(array,placeholder){var index=-1,length=array.length,resIndex=-1,result=[];while(++index>>1;var MAX_SAFE_INTEGER=9007199254740991;var metaMap=WeakMap&&new WeakMap;var realNames={};function lodash(value){if(isObjectLike(value)&&!isArray(value)&&!(value instanceof LazyWrapper)){if(value instanceof LodashWrapper){return value}if(hasOwnProperty.call(value,"__chain__")&&hasOwnProperty.call(value,"__wrapped__")){return wrapperClone(value)}}return new LodashWrapper(value)}function baseLodash(){}function LodashWrapper(value,chainAll,actions){this.__wrapped__=value;this.__actions__=actions||[];this.__chain__=!!chainAll}var support=lodash.support={};lodash.templateSettings={escape:reEscape,evaluate:reEvaluate,interpolate:reInterpolate,variable:"",imports:{_:lodash}};function LazyWrapper(value){this.__wrapped__=value;this.__actions__=[];this.__dir__=1;this.__filtered__=false;this.__iteratees__=[];this.__takeCount__=POSITIVE_INFINITY;this.__views__=[]}function lazyClone(){var result=new LazyWrapper(this.__wrapped__);result.__actions__=arrayCopy(this.__actions__);result.__dir__=this.__dir__;result.__filtered__=this.__filtered__;result.__iteratees__=arrayCopy(this.__iteratees__);result.__takeCount__=this.__takeCount__;result.__views__=arrayCopy(this.__views__);return result}function lazyReverse(){if(this.__filtered__){var result=new LazyWrapper(this);result.__dir__=-1;result.__filtered__=true}else{result=this.clone();result.__dir__*=-1}return result}function lazyValue(){var array=this.__wrapped__.value(),dir=this.__dir__,isArr=isArray(array),isRight=dir<0,arrLength=isArr?array.length:0,view=getView(0,arrLength,this.__views__),start=view.start,end=view.end,length=end-start,index=isRight?end:start-1,iteratees=this.__iteratees__,iterLength=iteratees.length,resIndex=0,takeCount=nativeMin(length,this.__takeCount__);if(!isArr||arrLength=LARGE_ARRAY_SIZE?createCache(values):null,valuesLength=values.length;if(cache){indexOf=cacheIndexOf;isCommon=false;values=cache}outer:while(++indexlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end>>>0;start>>>=0;while(startlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end-start>>>0;start>>>=0;var result=Array(length);while(++index=LARGE_ARRAY_SIZE,seen=isLarge?createCache():null,result=[];if(seen){indexOf=cacheIndexOf;isCommon=false}else{isLarge=false;seen=iteratee?[]:result}outer:while(++index>>1,computed=array[mid];if((retHighest?computed<=value:computed2?sources[length-2]:undefined,guard=length>2?sources[2]:undefined,thisArg=length>1?sources[length-1]:undefined;if(typeof customizer=="function"){customizer=bindCallback(customizer,thisArg,5);length-=2}else{customizer=typeof thisArg=="function"?thisArg:undefined;length-=customizer?1:0}if(guard&&isIterateeCall(sources[0],sources[1],guard)){customizer=length<3?undefined:customizer;length=1}while(++index-1?collection[index]:undefined}return baseFind(collection,predicate,eachFunc)}}function createFindIndex(fromRight){return function(array,predicate,thisArg){if(!(array&&array.length)){return-1}predicate=getCallback(predicate,thisArg,3);return baseFindIndex(array,predicate,fromRight)}}function createFindKey(objectFunc){return function(object,predicate,thisArg){predicate=getCallback(predicate,thisArg,3);return baseFind(object,predicate,objectFunc,true)}}function createFlow(fromRight){return function(){var wrapper,length=arguments.length,index=fromRight?length:-1,leftIndex=0,funcs=Array(length);while(fromRight?index--:++index=LARGE_ARRAY_SIZE){return wrapper.plant(value).value()}var index=0,result=length?funcs[index].apply(this,args):value;while(++index=length||!nativeIsFinite(length)){return""}var padLength=length-strLength;chars=chars==null?" ":chars+"";return repeat(chars,nativeCeil(padLength/chars.length)).slice(0,padLength)}function createPartialWrapper(func,bitmask,thisArg,partials){var isBind=bitmask&BIND_FLAG,Ctor=createCtorWrapper(func);function wrapper(){var argsIndex=-1,argsLength=arguments.length,leftIndex=-1,leftLength=partials.length,args=Array(leftLength+argsLength);while(++leftIndexarrLength)){return false}while(++index-1&&value%1==0&&value-1&&value%1==0&&value<=MAX_SAFE_INTEGER}function isStrictComparable(value){return value===value&&!isObject(value)}function mergeData(data,source){var bitmask=data[1],srcBitmask=source[1],newBitmask=bitmask|srcBitmask,isCommon=newBitmask0){if(++count>=HOT_COUNT){return key}}else{count=0}return baseSetData(key,value)}}();function shimKeys(object){var props=keysIn(object),propsLength=props.length,length=propsLength&&object.length;var allowIndexes=!!length&&isLength(length)&&(isArray(object)||isArguments(object));var index=-1,result=[];while(++index=120?createCache(othIndex&&value):null}var array=arrays[0],index=-1,length=array?array.length:0,seen=caches[0];outer:while(++index-1){splice.call(array,fromIndex,1)}}return array}var pullAt=restParam(function(array,indexes){indexes=baseFlatten(indexes);var result=baseAt(array,indexes);basePullAt(array,indexes.sort(baseCompareAscending));return result});function remove(array,predicate,thisArg){var result=[];if(!(array&&array.length)){return result}var index=-1,indexes=[],length=array.length;predicate=getCallback(predicate,thisArg,3);while(++index2?arrays[length-2]:undefined,thisArg=length>1?arrays[length-1]:undefined;if(length>2&&typeof iteratee=="function"){length-=2}else{iteratee=length>1&&typeof thisArg=="function"?(--length,thisArg):undefined;thisArg=undefined}arrays.length=length;return unzipWith(arrays,iteratee,thisArg)});function chain(value){var result=lodash(value);result.__chain__=true;return result}function tap(value,interceptor,thisArg){interceptor.call(thisArg,value);return value}function thru(value,interceptor,thisArg){return interceptor.call(thisArg,value)}function wrapperChain(){return chain(this)}function wrapperCommit(){return new LodashWrapper(this.value(),this.__chain__)}var wrapperConcat=restParam(function(values){values=baseFlatten(values);return this.thru(function(array){return arrayConcat(isArray(array)?array:[toObject(array)],values)})});function wrapperPlant(value){var result,parent=this;while(parent instanceof baseLodash){var clone=wrapperClone(parent);if(result){previous.__wrapped__=clone}else{result=clone}var previous=clone;parent=parent.__wrapped__}previous.__wrapped__=value;return result}function wrapperReverse(){var value=this.__wrapped__;var interceptor=function(value){return wrapped&&wrapped.__dir__<0?value:value.reverse()};if(value instanceof LazyWrapper){var wrapped=value;if(this.__actions__.length){wrapped=new LazyWrapper(this)}wrapped=wrapped.reverse();wrapped.__actions__.push({func:thru,args:[interceptor],thisArg:undefined});return new LodashWrapper(wrapped,this.__chain__)}return this.thru(interceptor)}function wrapperToString(){return this.value()+""}function wrapperValue(){return baseWrapperValue(this.__wrapped__,this.__actions__)}var at=restParam(function(collection,props){return baseAt(collection,baseFlatten(props))});var countBy=createAggregator(function(result,value,key){hasOwnProperty.call(result,key)?++result[key]:result[key]=1});function every(collection,predicate,thisArg){var func=isArray(collection)?arrayEvery:baseEvery;if(thisArg&&isIterateeCall(collection,predicate,thisArg)){predicate=undefined}if(typeof predicate!="function"||thisArg!==undefined){predicate=getCallback(predicate,thisArg,3)}return func(collection,predicate)}function filter(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,predicate)}var find=createFind(baseEach);var findLast=createFind(baseEachRight,true);function findWhere(collection,source){return find(collection,baseMatches(source))}var forEach=createForEach(arrayEach,baseEach);var forEachRight=createForEach(arrayEachRight,baseEachRight);var groupBy=createAggregator(function(result,value,key){if(hasOwnProperty.call(result,key)){result[key].push(value)}else{result[key]=[value]}});function includes(collection,target,fromIndex,guard){var length=collection?getLength(collection):0;if(!isLength(length)){collection=values(collection);length=collection.length}if(typeof fromIndex!="number"||guard&&isIterateeCall(target,fromIndex,guard)){fromIndex=0}else{fromIndex=fromIndex<0?nativeMax(length+fromIndex,0):fromIndex||0}return typeof collection=="string"||!isArray(collection)&&isString(collection)?fromIndex<=length&&collection.indexOf(target,fromIndex)>-1:!!length&&getIndexOf(collection,target,fromIndex)>-1}var indexBy=createAggregator(function(result,value,key){result[key]=value});var invoke=restParam(function(collection,path,args){var index=-1,isFunc=typeof path=="function",isProp=isKey(path),result=isArrayLike(collection)?Array(collection.length):[];baseEach(collection,function(value){var func=isFunc?path:isProp&&value!=null?value[path]:undefined;result[++index]=func?func.apply(value,args):invokePath(value,path,args)});return result});function map(collection,iteratee,thisArg){var func=isArray(collection)?arrayMap:baseMap;iteratee=getCallback(iteratee,thisArg,3);return func(collection,iteratee)}var partition=createAggregator(function(result,value,key){result[key?0:1].push(value)},function(){return[[],[]]});function pluck(collection,path){return map(collection,property(path))}var reduce=createReduce(arrayReduce,baseEach);var reduceRight=createReduce(arrayReduceRight,baseEachRight);function reject(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,function(value,index,collection){return!predicate(value,index,collection)})}function sample(collection,n,guard){if(guard?isIterateeCall(collection,n,guard):n==null){collection=toIterable(collection);var length=collection.length;return length>0?collection[baseRandom(0,length-1)]:undefined}var index=-1,result=toArray(collection),length=result.length,lastIndex=length-1;n=nativeMin(n<0?0:+n||0,length);while(++index0){result=func.apply(this,arguments)}if(n<=1){func=undefined}return result}}var bind=restParam(function(func,thisArg,partials){var bitmask=BIND_FLAG;if(partials.length){var holders=replaceHolders(partials,bind.placeholder);bitmask|=PARTIAL_FLAG}return createWrapper(func,bitmask,thisArg,partials,holders)});var bindAll=restParam(function(object,methodNames){methodNames=methodNames.length?baseFlatten(methodNames):functions(object);var index=-1,length=methodNames.length;while(++indexwait){complete(trailingCall,maxTimeoutId)}else{timeoutId=setTimeout(delayed,remaining)}}function maxDelayed(){complete(trailing,timeoutId)}function debounced(){args=arguments;stamp=now();thisArg=this;trailingCall=trailing&&(timeoutId||!leading);if(maxWait===false){var leadingCall=leading&&!timeoutId}else{if(!maxTimeoutId&&!leading){lastCalled=stamp}var remaining=maxWait-(stamp-lastCalled),isCalled=remaining<=0||remaining>maxWait;if(isCalled){if(maxTimeoutId){maxTimeoutId=clearTimeout(maxTimeoutId)}lastCalled=stamp;result=func.apply(thisArg,args)}else if(!maxTimeoutId){maxTimeoutId=setTimeout(maxDelayed,remaining)}}if(isCalled&&timeoutId){timeoutId=clearTimeout(timeoutId)}else if(!timeoutId&&wait!==maxWait){timeoutId=setTimeout(delayed,wait)}if(leadingCall){isCalled=true;result=func.apply(thisArg,args)}if(isCalled&&!timeoutId&&!maxTimeoutId){args=thisArg=undefined}return result}debounced.cancel=cancel;return debounced}var defer=restParam(function(func,args){return baseDelay(func,1,args)});var delay=restParam(function(func,wait,args){return baseDelay(func,wait,args)});var flow=createFlow();var flowRight=createFlow(true);function memoize(func,resolver){if(typeof func!="function"||resolver&&typeof resolver!="function"){throw new TypeError(FUNC_ERROR_TEXT)}var memoized=function(){var args=arguments,key=resolver?resolver.apply(this,args):args[0],cache=memoized.cache;if(cache.has(key)){return cache.get(key)}var result=func.apply(this,args);memoized.cache=cache.set(key,result);return result};memoized.cache=new memoize.Cache;return memoized}var modArgs=restParam(function(func,transforms){transforms=baseFlatten(transforms);if(typeof func!="function"||!arrayEvery(transforms,baseIsFunction)){throw new TypeError(FUNC_ERROR_TEXT)}var length=transforms.length;return restParam(function(args){var index=nativeMin(args.length,length);while(index--){args[index]=transforms[index](args[index])}return func.apply(this,args)})});function negate(predicate){if(typeof predicate!="function"){throw new TypeError(FUNC_ERROR_TEXT)}return function(){return!predicate.apply(this,arguments)}}function once(func){return before(2,func)}var partial=createPartial(PARTIAL_FLAG);var partialRight=createPartial(PARTIAL_RIGHT_FLAG);var rearg=restParam(function(func,indexes){return createWrapper(func,REARG_FLAG,undefined,undefined,undefined,baseFlatten(indexes))});function restParam(func,start){if(typeof func!="function"){throw new TypeError(FUNC_ERROR_TEXT)}start=nativeMax(start===undefined?func.length-1:+start||0,0);return function(){var args=arguments,index=-1,length=nativeMax(args.length-start,0),rest=Array(length);while(++indexother}function gte(value,other){return value>=other}function isArguments(value){return isObjectLike(value)&&isArrayLike(value)&&hasOwnProperty.call(value,"callee")&&!propertyIsEnumerable.call(value,"callee")}var isArray=nativeIsArray||function(value){return isObjectLike(value)&&isLength(value.length)&&objToString.call(value)==arrayTag};function isBoolean(value){return value===true||value===false||isObjectLike(value)&&objToString.call(value)==boolTag}function isDate(value){return isObjectLike(value)&&objToString.call(value)==dateTag}function isElement(value){return!!value&&value.nodeType===1&&isObjectLike(value)&&!isPlainObject(value)}function isEmpty(value){if(value==null){return true}if(isArrayLike(value)&&(isArray(value)||isString(value)||isArguments(value)||isObjectLike(value)&&isFunction(value.splice))){return!value.length}return!keys(value).length}function isEqual(value,other,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;var result=customizer?customizer(value,other):undefined;return result===undefined?baseIsEqual(value,other,customizer):!!result}function isError(value){return isObjectLike(value)&&typeof value.message=="string"&&objToString.call(value)==errorTag}function isFinite(value){return typeof value=="number"&&nativeIsFinite(value)}function isFunction(value){return isObject(value)&&objToString.call(value)==funcTag}function isObject(value){var type=typeof value;return!!value&&(type=="object"||type=="function")}function isMatch(object,source,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;return baseIsMatch(object,getMatchData(source),customizer)}function isNaN(value){return isNumber(value)&&value!=+value}function isNative(value){if(value==null){return false}if(isFunction(value)){return reIsNative.test(fnToString.call(value))}return isObjectLike(value)&&reIsHostCtor.test(value)}function isNull(value){return value===null}function isNumber(value){return typeof value=="number"||isObjectLike(value)&&objToString.call(value)==numberTag}function isPlainObject(value){var Ctor;if(!(isObjectLike(value)&&objToString.call(value)==objectTag&&!isArguments(value))||!hasOwnProperty.call(value,"constructor")&&(Ctor=value.constructor,typeof Ctor=="function"&&!(Ctor instanceof Ctor))){return false}var result;baseForIn(value,function(subValue,key){result=key});return result===undefined||hasOwnProperty.call(value,result)}function isRegExp(value){return isObject(value)&&objToString.call(value)==regexpTag}function isString(value){return typeof value=="string"||isObjectLike(value)&&objToString.call(value)==stringTag}function isTypedArray(value){return isObjectLike(value)&&isLength(value.length)&&!!typedArrayTags[objToString.call(value)]}function isUndefined(value){return value===undefined}function lt(value,other){return value0;while(++index=nativeMin(start,end)&&value=0&&string.indexOf(target,position)==position}function escape(string){string=baseToString(string);return string&&reHasUnescapedHtml.test(string)?string.replace(reUnescapedHtml,escapeHtmlChar):string}function escapeRegExp(string){string=baseToString(string);return string&&reHasRegExpChars.test(string)?string.replace(reRegExpChars,escapeRegExpChar):string||"(?:)"}var kebabCase=createCompounder(function(result,word,index){return result+(index?"-":"")+word.toLowerCase()});function pad(string,length,chars){string=baseToString(string);length=+length;var strLength=string.length;if(strLength>=length||!nativeIsFinite(length)){return string}var mid=(length-strLength)/2,leftLength=nativeFloor(mid),rightLength=nativeCeil(mid);chars=createPadding("",rightLength,chars);return chars.slice(0,leftLength)+string+chars}var padLeft=createPadDir();var padRight=createPadDir(true);function parseInt(string,radix,guard){if(guard?isIterateeCall(string,radix,guard):radix==null){radix=0}else if(radix){radix=+radix}string=trim(string);return nativeParseInt(string,radix||(reHasHexPrefix.test(string)?16:10))}function repeat(string,n){var result="";string=baseToString(string);n=+n;if(n<1||!string||!nativeIsFinite(n)){return result}do{if(n%2){result+=string}n=nativeFloor(n/2);string+=string}while(n);return result}var snakeCase=createCompounder(function(result,word,index){return result+(index?"_":"")+word.toLowerCase()});var startCase=createCompounder(function(result,word,index){return result+(index?" ":"")+(word.charAt(0).toUpperCase()+word.slice(1))});function startsWith(string,target,position){string=baseToString(string);position=position==null?0:nativeMin(position<0?0:+position||0,string.length);return string.lastIndexOf(target,position)==position}function template(string,options,otherOptions){var settings=lodash.templateSettings;if(otherOptions&&isIterateeCall(string,options,otherOptions)){options=otherOptions=undefined}string=baseToString(string);options=assignWith(baseAssign({},otherOptions||options),settings,assignOwnDefaults);var imports=assignWith(baseAssign({},options.imports),settings.imports,assignOwnDefaults),importsKeys=keys(imports),importsValues=baseValues(imports,importsKeys);var isEscaping,isEvaluating,index=0,interpolate=options.interpolate||reNoMatch,source="__p += '";var reDelimiters=RegExp((options.escape||reNoMatch).source+"|"+interpolate.source+"|"+(interpolate===reInterpolate?reEsTemplate:reNoMatch).source+"|"+(options.evaluate||reNoMatch).source+"|$","g");var sourceURL="//# sourceURL="+("sourceURL"in options?options.sourceURL:"lodash.templateSources["+ ++templateCounter+"]")+"\n";string.replace(reDelimiters,function(match,escapeValue,interpolateValue,esTemplateValue,evaluateValue,offset){interpolateValue||(interpolateValue=esTemplateValue);source+=string.slice(index,offset).replace(reUnescapedString,escapeStringChar);if(escapeValue){isEscaping=true;source+="' +\n__e("+escapeValue+") +\n'"}if(evaluateValue){isEvaluating=true;source+="';\n"+evaluateValue+";\n__p += '"}if(interpolateValue){source+="' +\n((__t = ("+interpolateValue+")) == null ? '' : __t) +\n'"}index=offset+match.length;return match});source+="';\n";var variable=options.variable;if(!variable){source="with (obj) {\n"+source+"\n}\n"}source=(isEvaluating?source.replace(reEmptyStringLeading,""):source).replace(reEmptyStringMiddle,"$1").replace(reEmptyStringTrailing,"$1;");source="function("+(variable||"obj")+") {\n"+(variable?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(isEscaping?", __e = _.escape":"")+(isEvaluating?", __j = Array.prototype.join;\n"+"function print() { __p += __j.call(arguments, '') }\n":";\n")+source+"return __p\n}";var result=attempt(function(){return Function(importsKeys,sourceURL+"return "+source).apply(undefined,importsValues)});result.source=source;if(isError(result)){throw result}return result}function trim(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(trimmedLeftIndex(string),trimmedRightIndex(string)+1)}chars=chars+"";return string.slice(charsLeftIndex(string,chars),charsRightIndex(string,chars)+1)}function trimLeft(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(trimmedLeftIndex(string))}return string.slice(charsLeftIndex(string,chars+""))}function trimRight(string,chars,guard){var value=string;string=baseToString(string);if(!string){return string}if(guard?isIterateeCall(value,chars,guard):chars==null){return string.slice(0,trimmedRightIndex(string)+1)}return string.slice(0,charsRightIndex(string,chars+"")+1)}function trunc(string,options,guard){if(guard&&isIterateeCall(string,options,guard)){options=undefined}var length=DEFAULT_TRUNC_LENGTH,omission=DEFAULT_TRUNC_OMISSION;if(options!=null){if(isObject(options)){var separator="separator"in options?options.separator:separator;length="length"in options?+options.length||0:length;omission="omission"in options?baseToString(options.omission):omission}else{length=+options||0}}string=baseToString(string);if(length>=string.length){return string}var end=length-omission.length;if(end<1){return omission}var result=string.slice(0,end);if(separator==null){return result+omission}if(isRegExp(separator)){if(string.slice(end).search(separator)){var match,newEnd,substring=string.slice(0,end);if(!separator.global){separator=RegExp(separator.source,(reFlags.exec(separator)||"")+"g")}separator.lastIndex=0;while(match=separator.exec(substring)){newEnd=match.index}result=result.slice(0,newEnd==null?end:newEnd)}}else if(string.indexOf(separator,end)!=end){var index=result.lastIndexOf(separator);if(index>-1){result=result.slice(0,index)}}return result+omission}function unescape(string){string=baseToString(string);return string&&reHasEscapedHtml.test(string)?string.replace(reEscapedHtml,unescapeHtmlChar):string}function words(string,pattern,guard){if(guard&&isIterateeCall(string,pattern,guard)){pattern=undefined}string=baseToString(string);return string.match(pattern||reWords)||[]}var attempt=restParam(function(func,args){try{return func.apply(undefined,args)}catch(e){return isError(e)?e:new Error(e)}});function callback(func,thisArg,guard){if(guard&&isIterateeCall(func,thisArg,guard)){thisArg=undefined}return isObjectLike(func)?matches(func):baseCallback(func,thisArg)}function constant(value){return function(){return value}}function identity(value){return value}function matches(source){return baseMatches(baseClone(source,true))}function matchesProperty(path,srcValue){return baseMatchesProperty(path,baseClone(srcValue,true))}var method=restParam(function(path,args){return function(object){return invokePath(object,path,args)}});var methodOf=restParam(function(object,args){return function(path){return invokePath(object,path,args)}});function mixin(object,source,options){if(options==null){var isObj=isObject(source),props=isObj?keys(source):undefined,methodNames=props&&props.length?baseFunctions(source,props):undefined;if(!(methodNames?methodNames.length:isObj)){methodNames=false;options=source;source=object;object=this}}if(!methodNames){methodNames=baseFunctions(source,keys(source))}var chain=true,index=-1,isFunc=isFunction(object),length=methodNames.length;if(options===false){chain=false}else if(isObject(options)&&"chain"in options){chain=options.chain}while(++index0||end<0)){return new LazyWrapper(result)}if(start<0){result=result.takeRight(-start)}else if(start){result=result.drop(start)}if(end!==undefined){end=+end||0;result=end<0?result.dropRight(-end):result.take(end-start)}return result};LazyWrapper.prototype.takeRightWhile=function(predicate,thisArg){return this.reverse().takeWhile(predicate,thisArg).reverse()};LazyWrapper.prototype.toArray=function(){return this.take(POSITIVE_INFINITY)};baseForOwn(LazyWrapper.prototype,function(func,methodName){var checkIteratee=/^(?:filter|map|reject)|While$/.test(methodName),retUnwrapped=/^(?:first|last)$/.test(methodName),lodashFunc=lodash[retUnwrapped?"take"+(methodName=="last"?"Right":""):methodName];if(!lodashFunc){return}lodash.prototype[methodName]=function(){var args=retUnwrapped?[1]:arguments,chainAll=this.__chain__,value=this.__wrapped__,isHybrid=!!this.__actions__.length,isLazy=value instanceof LazyWrapper,iteratee=args[0],useLazy=isLazy||isArray(value);if(useLazy&&checkIteratee&&typeof iteratee=="function"&&iteratee.length!=1){isLazy=useLazy=false}var interceptor=function(value){return retUnwrapped&&chainAll?lodashFunc(value,1)[0]:lodashFunc.apply(undefined,arrayPush([value],args))};var action={func:thru,args:[interceptor],thisArg:undefined},onlyLazy=isLazy&&!isHybrid;if(retUnwrapped&&!chainAll){if(onlyLazy){value=value.clone();value.__actions__.push(action);return func.call(value)}return lodashFunc.call(undefined,this.value())[0]}if(!retUnwrapped&&useLazy){value=onlyLazy?value:new LazyWrapper(this);var result=func.apply(value,args);result.__actions__.push(action);return new LodashWrapper(result,chainAll)}return this.thru(interceptor)}});arrayEach(["join","pop","push","replace","shift","sort","splice","split","unshift"],function(methodName){var func=(/^(?:replace|split)$/.test(methodName)?stringProto:arrayProto)[methodName],chainName=/^(?:push|sort|unshift)$/.test(methodName)?"tap":"thru",retUnwrapped=/^(?:join|pop|replace|shift)$/.test(methodName);lodash.prototype[methodName]=function(){var args=arguments;if(retUnwrapped&&!this.__chain__){return func.apply(this.value(),args)}return this[chainName](function(value){return func.apply(value,args)})}});baseForOwn(LazyWrapper.prototype,function(func,methodName){var lodashFunc=lodash[methodName];if(lodashFunc){var key=lodashFunc.name,names=realNames[key]||(realNames[key]=[]);names.push({name:methodName,func:lodashFunc})}});realNames[createHybridWrapper(undefined,BIND_KEY_FLAG).name]=[{name:"wrapper",func:undefined}];LazyWrapper.prototype.clone=lazyClone;LazyWrapper.prototype.reverse=lazyReverse;LazyWrapper.prototype.value=lazyValue;lodash.prototype.chain=wrapperChain;lodash.prototype.commit=wrapperCommit;lodash.prototype.concat=wrapperConcat;lodash.prototype.plant=wrapperPlant;lodash.prototype.reverse=wrapperReverse;lodash.prototype.toString=wrapperToString;lodash.prototype.run=lodash.prototype.toJSON=lodash.prototype.valueOf=lodash.prototype.value=wrapperValue;lodash.prototype.collect=lodash.prototype.map;lodash.prototype.head=lodash.prototype.first;lodash.prototype.select=lodash.prototype.filter;lodash.prototype.tail=lodash.prototype.rest;return lodash}var _=runInContext();if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){root._=_;define(function(){return _})}else if(freeExports&&freeModule){if(moduleExports){(freeModule.exports=_)._=_}else{freeExports._=_}}else{root._=_}}).call(this)}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],3:[function(require,module,exports){(function(window,document,undefined){var _MAP={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"};var _KEYCODE_MAP={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"};var _SHIFT_MAP={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"};var _SPECIAL_ALIASES={option:"alt",command:"meta",return:"enter",escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"};var _REVERSE_MAP;for(var i=1;i<20;++i){_MAP[111+i]="f"+i}for(i=0;i<=9;++i){_MAP[i+96]=i}function _addEvent(object,type,callback){if(object.addEventListener){object.addEventListener(type,callback,false);return}object.attachEvent("on"+type,callback)}function _characterFromEvent(e){if(e.type=="keypress"){var character=String.fromCharCode(e.which);if(!e.shiftKey){character=character.toLowerCase()}return character}if(_MAP[e.which]){return _MAP[e.which]}if(_KEYCODE_MAP[e.which]){return _KEYCODE_MAP[e.which]}return String.fromCharCode(e.which).toLowerCase()}function _modifiersMatch(modifiers1,modifiers2){return modifiers1.sort().join(",")===modifiers2.sort().join(",")}function _eventModifiers(e){var modifiers=[];if(e.shiftKey){modifiers.push("shift")}if(e.altKey){modifiers.push("alt")}if(e.ctrlKey){modifiers.push("ctrl")}if(e.metaKey){modifiers.push("meta")}return modifiers}function _preventDefault(e){if(e.preventDefault){e.preventDefault();return}e.returnValue=false}function _stopPropagation(e){if(e.stopPropagation){e.stopPropagation();return}e.cancelBubble=true}function _isModifier(key){return key=="shift"||key=="ctrl"||key=="alt"||key=="meta"}function _getReverseMap(){if(!_REVERSE_MAP){_REVERSE_MAP={};for(var key in _MAP){if(key>95&&key<112){continue}if(_MAP.hasOwnProperty(key)){_REVERSE_MAP[_MAP[key]]=key}}}return _REVERSE_MAP}function _pickBestAction(key,modifiers,action){if(!action){action=_getReverseMap()[key]?"keydown":"keypress"}if(action=="keypress"&&modifiers.length){action="keydown"}return action}function _keysFromString(combination){if(combination==="+"){return["+"]}combination=combination.replace(/\+{2}/g,"+plus");return combination.split("+")}function _getKeyInfo(combination,action){var keys;var key;var i;var modifiers=[];keys=_keysFromString(combination);for(i=0;i1){_bindSequence(combination,sequence,callback,action);return}info=_getKeyInfo(combination,action);self._callbacks[info.key]=self._callbacks[info.key]||[];_getMatches(info.key,info.modifiers,{type:info.action},sequenceName,combination,level);self._callbacks[info.key][sequenceName?"unshift":"push"]({callback:callback,modifiers:info.modifiers,action:info.action,seq:sequenceName,level:level,combo:combination})}self._bindMultiple=function(combinations,callback,action){for(var i=0;i-1){return false}if(_belongsTo(element,self.target)){return false}return element.tagName=="INPUT"||element.tagName=="SELECT"||element.tagName=="TEXTAREA"||element.isContentEditable};Mousetrap.prototype.handleKey=function(){var self=this;return self._handleKey.apply(self,arguments)};Mousetrap.init=function(){var documentMousetrap=Mousetrap(document);for(var method in documentMousetrap){if(method.charAt(0)!=="_"){Mousetrap[method]=function(method){return function(){return documentMousetrap[method].apply(documentMousetrap,arguments)}}(method)}}};Mousetrap.init();window.Mousetrap=Mousetrap;if(typeof module!=="undefined"&&module.exports){module.exports=Mousetrap}if(typeof define==="function"&&define.amd){define(function(){return Mousetrap})}})(window,document)},{}],4:[function(require,module,exports){(function(process){function normalizeArray(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;var splitPath=function(filename){return splitPathRe.exec(filename).slice(1)};exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:process.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.isAbsolute=function(path){return path.charAt(0)==="/"};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=="string"){throw new TypeError("Arguments to path.join must be strings")}return p}).join("/"))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i1){for(var i=1;i= 0x80 (not a basic code point)","invalid-input":"Invalid input"},baseMinusTMin=base-tMin,floor=Math.floor,stringFromCharCode=String.fromCharCode,key;function error(type){throw RangeError(errors[type])}function map(array,fn){var length=array.length;var result=[];while(length--){result[length]=fn(array[length])}return result}function mapDomain(string,fn){var parts=string.split("@");var result="";if(parts.length>1){result=parts[0]+"@";string=parts[1]}string=string.replace(regexSeparators,".");var labels=string.split(".");var encoded=map(labels,fn).join(".");return result+encoded}function ucs2decode(string){var output=[],counter=0,length=string.length,value,extra;while(counter=55296&&value<=56319&&counter65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value);return output}).join("")}function basicToDigit(codePoint){if(codePoint-48<10){return codePoint-22}if(codePoint-65<26){return codePoint-65}if(codePoint-97<26){return codePoint-97}return base}function digitToBasic(digit,flag){return digit+22+75*(digit<26)-((flag!=0)<<5)}function adapt(delta,numPoints,firstTime){var k=0;delta=firstTime?floor(delta/damp):delta>>1;delta+=floor(delta/numPoints);for(;delta>baseMinusTMin*tMax>>1;k+=base){delta=floor(delta/baseMinusTMin)}return floor(k+(baseMinusTMin+1)*delta/(delta+skew))}function decode(input){var output=[],inputLength=input.length,out,i=0,n=initialN,bias=initialBias,basic,j,index,oldi,w,k,digit,t,baseMinusT;basic=input.lastIndexOf(delimiter);if(basic<0){basic=0}for(j=0;j=128){error("not-basic")}output.push(input.charCodeAt(j))}for(index=basic>0?basic+1:0;index=inputLength){error("invalid-input")}digit=basicToDigit(input.charCodeAt(index++));if(digit>=base||digit>floor((maxInt-i)/w)){error("overflow")}i+=digit*w;t=k<=bias?tMin:k>=bias+tMax?tMax:k-bias;if(digitfloor(maxInt/baseMinusT)){error("overflow")}w*=baseMinusT}out=output.length+1;bias=adapt(i-oldi,out,oldi==0);if(floor(i/out)>maxInt-n){error("overflow")}n+=floor(i/out);i%=out;output.splice(i++,0,n)}return ucs2encode(output)}function encode(input){var n,delta,handledCPCount,basicLength,bias,j,m,q,k,t,currentValue,output=[],inputLength,handledCPCountPlusOne,baseMinusT,qMinusT;input=ucs2decode(input);inputLength=input.length;n=initialN;delta=0;bias=initialBias;for(j=0;j=n&¤tValuefloor((maxInt-delta)/handledCPCountPlusOne)){error("overflow")}delta+=(m-n)*handledCPCountPlusOne;n=m;for(j=0;jmaxInt){error("overflow")}if(currentValue==n){for(q=delta,k=base;;k+=base){t=k<=bias?tMin:k>=bias+tMax?tMax:k-bias;if(q0&&len>maxKeys){len=maxKeys}for(var i=0;i=0){kstr=x.substr(0,idx);vstr=x.substr(idx+1)}else{kstr=x;vstr=""}k=decodeURIComponent(kstr);v=decodeURIComponent(vstr);if(!hasOwnProperty(obj,k)){obj[k]=v}else if(isArray(obj[k])){obj[k].push(v)}else{obj[k]=[obj[k],v]}}return obj};var isArray=Array.isArray||function(xs){return Object.prototype.toString.call(xs)==="[object Array]"}},{}],8:[function(require,module,exports){"use strict";var stringifyPrimitive=function(v){switch(typeof v){case"string":return v;case"boolean":return v?"true":"false";case"number":return isFinite(v)?v:"";default:return""}};module.exports=function(obj,sep,eq,name){sep=sep||"&";eq=eq||"=";if(obj===null){obj=undefined}if(typeof obj==="object"){return map(objectKeys(obj),function(k){var ks=encodeURIComponent(stringifyPrimitive(k))+eq;if(isArray(obj[k])){return map(obj[k],function(v){return ks+encodeURIComponent(stringifyPrimitive(v))}).join(sep)}else{return ks+encodeURIComponent(stringifyPrimitive(obj[k]))}}).join(sep)}if(!name)return"";return encodeURIComponent(stringifyPrimitive(name))+eq+encodeURIComponent(stringifyPrimitive(obj))};var isArray=Array.isArray||function(xs){return Object.prototype.toString.call(xs)==="[object Array]"};function map(xs,f){if(xs.map)return xs.map(f);var res=[];for(var i=0;i",'"',"`"," ","\r","\n","\t"],unwise=["{","}","|","\\","^","`"].concat(delims),autoEscape=["'"].concat(unwise),nonHostChars=["%","/","?",";","#"].concat(autoEscape),hostEndingChars=["/","?","#"],hostnameMaxLen=255,hostnamePartPattern=/^[a-z0-9A-Z_-]{0,63}$/,hostnamePartStart=/^([a-z0-9A-Z_-]{0,63})(.*)$/,unsafeProtocol={javascript:true,"javascript:":true},hostlessProtocol={javascript:true,"javascript:":true},slashedProtocol={http:true,https:true,ftp:true,gopher:true,file:true,"http:":true,"https:":true,"ftp:":true,"gopher:":true,"file:":true},querystring=require("querystring");function urlParse(url,parseQueryString,slashesDenoteHost){if(url&&isObject(url)&&url instanceof Url)return url;var u=new Url;u.parse(url,parseQueryString,slashesDenoteHost);return u}Url.prototype.parse=function(url,parseQueryString,slashesDenoteHost){if(!isString(url)){throw new TypeError("Parameter 'url' must be a string, not "+typeof url)}var rest=url;rest=rest.trim();var proto=protocolPattern.exec(rest);if(proto){proto=proto[0];var lowerProto=proto.toLowerCase();this.protocol=lowerProto;rest=rest.substr(proto.length)}if(slashesDenoteHost||proto||rest.match(/^\/\/[^@\/]+@[^@\/]+/)){var slashes=rest.substr(0,2)==="//";if(slashes&&!(proto&&hostlessProtocol[proto])){rest=rest.substr(2);this.slashes=true}}if(!hostlessProtocol[proto]&&(slashes||proto&&!slashedProtocol[proto])){var hostEnd=-1;for(var i=0;i127){newpart+="x"}else{newpart+=part[j]}}if(!newpart.match(hostnamePartPattern)){var validParts=hostparts.slice(0,i);var notHost=hostparts.slice(i+1);var bit=part.match(hostnamePartStart);if(bit){validParts.push(bit[1]);notHost.unshift(bit[2])}if(notHost.length){rest="/"+notHost.join(".")+rest}this.hostname=validParts.join(".");break}}}}if(this.hostname.length>hostnameMaxLen){this.hostname=""}else{this.hostname=this.hostname.toLowerCase()}if(!ipv6Hostname){var domainArray=this.hostname.split(".");var newOut=[];for(var i=0;i0?result.host.split("@"):false;if(authInHost){result.auth=authInHost.shift();result.host=result.hostname=authInHost.shift()}}result.search=relative.search;result.query=relative.query;if(!isNull(result.pathname)||!isNull(result.search)){result.path=(result.pathname?result.pathname:"")+(result.search?result.search:"")}result.href=result.format();return result}if(!srcPath.length){result.pathname=null;if(result.search){result.path="/"+result.search}else{result.path=null}result.href=result.format();return result}var last=srcPath.slice(-1)[0];var hasTrailingSlash=(result.host||relative.host)&&(last==="."||last==="..")||last==="";var up=0;for(var i=srcPath.length;i>=0;i--){last=srcPath[i];if(last=="."){srcPath.splice(i,1)}else if(last===".."){srcPath.splice(i,1);up++}else if(up){srcPath.splice(i,1);up--}}if(!mustEndAbs&&!removeAllDots){for(;up--;up){srcPath.unshift("..")}}if(mustEndAbs&&srcPath[0]!==""&&(!srcPath[0]||srcPath[0].charAt(0)!=="/")){srcPath.unshift("")}if(hasTrailingSlash&&srcPath.join("/").substr(-1)!=="/"){srcPath.push("")}var isAbsolute=srcPath[0]===""||srcPath[0]&&srcPath[0].charAt(0)==="/";if(psychotic){result.hostname=result.host=isAbsolute?"":srcPath.length?srcPath.shift():"";var authInHost=result.host&&result.host.indexOf("@")>0?result.host.split("@"):false;if(authInHost){result.auth=authInHost.shift();result.host=result.hostname=authInHost.shift()}}mustEndAbs=mustEndAbs||result.host&&srcPath.length;if(mustEndAbs&&!isAbsolute){srcPath.unshift("")}if(!srcPath.length){result.pathname=null;result.path=null}else{result.pathname=srcPath.join("/")}if(!isNull(result.pathname)||!isNull(result.search)){result.path=(result.pathname?result.pathname:"")+(result.search?result.search:"")}result.auth=relative.auth||result.auth;result.slashes=result.slashes||relative.slashes;result.href=result.format();return result};Url.prototype.parseHost=function(){var host=this.host;var port=portPattern.exec(host);if(port){port=port[0];if(port!==":"){this.port=port.substr(1)}host=host.substr(0,host.length-port.length)}if(host)this.hostname=host};function isString(arg){return typeof arg==="string"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isNull(arg){return arg===null}function isNullOrUndefined(arg){return arg==null}},{punycode:6,querystring:9}],11:[function(require,module,exports){var $=require("jquery");function toggleDropdown(e){var $dropdown=$(e.currentTarget).parent().find(".dropdown-menu");$dropdown.toggleClass("open");e.stopPropagation();e.preventDefault()}function closeDropdown(e){$(".dropdown-menu").removeClass("open")}function init(){$(document).on("click",".toggle-dropdown",toggleDropdown);$(document).on("click",".dropdown-menu",function(e){e.stopPropagation()});$(document).on("click",closeDropdown)}module.exports={init:init}},{jquery:1}],12:[function(require,module,exports){var $=require("jquery");module.exports=$({})},{jquery:1}],13:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var storage=require("./storage");var dropdown=require("./dropdown");var events=require("./events");var state=require("./state");var keyboard=require("./keyboard");var navigation=require("./navigation");var sidebar=require("./sidebar");var toolbar=require("./toolbar");function start(config){sidebar.init();keyboard.init();dropdown.init();navigation.init();toolbar.createButton({index:0,icon:"fa fa-align-justify",label:"Toggle Sidebar",onClick:function(e){e.preventDefault();sidebar.toggle()}});events.trigger("start",config);navigation.notify()}var gitbook={start:start,events:events,state:state,toolbar:toolbar,sidebar:sidebar,storage:storage,keyboard:keyboard};var MODULES={gitbook:gitbook,jquery:$,lodash:_};window.gitbook=gitbook;window.$=$;window.jQuery=$;gitbook.require=function(mods,fn){mods=_.map(mods,function(mod){mod=mod.toLowerCase();if(!MODULES[mod]){throw new Error("GitBook module "+mod+" doesn't exist")}return MODULES[mod]});fn.apply(null,mods)};module.exports={}},{"./dropdown":11,"./events":12,"./keyboard":14,"./navigation":16,"./sidebar":18,"./state":19,"./storage":20,"./toolbar":21,jquery:1,lodash:2}],14:[function(require,module,exports){var Mousetrap=require("mousetrap");var navigation=require("./navigation");var sidebar=require("./sidebar");function bindShortcut(keys,fn){Mousetrap.bind(keys,function(e){fn();return false})}function init(){bindShortcut(["right"],function(e){navigation.goNext()});bindShortcut(["left"],function(e){navigation.goPrev()});bindShortcut(["s"],function(e){sidebar.toggle()})}module.exports={init:init,bind:bindShortcut}},{"./navigation":16,"./sidebar":18,mousetrap:3}],15:[function(require,module,exports){var state=require("./state");function showLoading(p){state.$book.addClass("is-loading");p.always(function(){state.$book.removeClass("is-loading")});return p}module.exports={show:showLoading}},{"./state":19}],16:[function(require,module,exports){var $=require("jquery");var url=require("url");var events=require("./events");var state=require("./state");var loading=require("./loading");var usePushState=typeof history.pushState!=="undefined";function handleNavigation(relativeUrl,push){var uri=url.resolve(window.location.pathname,relativeUrl);notifyPageChange();location.href=relativeUrl;return}function updateNavigationPosition(){var bodyInnerWidth,pageWrapperWidth;bodyInnerWidth=parseInt($(".body-inner").css("width"),10);pageWrapperWidth=parseInt($(".page-wrapper").css("width"),10);$(".navigation-next").css("margin-right",bodyInnerWidth-pageWrapperWidth+"px")}function notifyPageChange(){events.trigger("page.change")}function preparePage(notify){var $bookBody=$(".book-body");var $bookInner=$bookBody.find(".body-inner");var $pageWrapper=$bookInner.find(".page-wrapper");updateNavigationPosition();$bookInner.scrollTop(0);$bookBody.scrollTop(0);if(notify!==false)notifyPageChange()}function isLeftClickEvent(e){return e.button===0}function isModifiedEvent(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function handlePagination(e){if(isModifiedEvent(e)||!isLeftClickEvent(e)){return}e.stopPropagation();e.preventDefault();var url=$(this).attr("href");if(url)handleNavigation(url,true)}function goNext(){var url=$(".navigation-next").attr("href");if(url)handleNavigation(url,true)}function goPrev(){var url=$(".navigation-prev").attr("href");if(url)handleNavigation(url,true)}function init(){$.ajaxSetup({});if(location.protocol!=="file:"){history.replaceState({path:window.location.href},"")}window.onpopstate=function(event){if(event.state===null){return}return handleNavigation(event.state.path,false)};$(document).on("click",".navigation-prev",handlePagination);$(document).on("click",".navigation-next",handlePagination);$(document).on("click",".summary [data-path] a",handlePagination);$(window).resize(updateNavigationPosition);preparePage(false)}module.exports={init:init,goNext:goNext,goPrev:goPrev,notify:notifyPageChange}},{"./events":12,"./loading":15,"./state":19,jquery:1,url:10}],17:[function(require,module,exports){module.exports={isMobile:function(){return document.body.clientWidth<=600}}},{}],18:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var storage=require("./storage");var platform=require("./platform");var state=require("./state");function toggleSidebar(_state,animation){if(state!=null&&isOpen()==_state)return;if(animation==null)animation=true;state.$book.toggleClass("without-animation",!animation);state.$book.toggleClass("with-summary",_state);storage.set("sidebar",isOpen())}function isOpen(){return state.$book.hasClass("with-summary")}function init(){if(platform.isMobile()){toggleSidebar(false,false)}else{toggleSidebar(storage.get("sidebar",true),false)}$(document).on("click",".book-summary li.chapter a",function(e){if(platform.isMobile())toggleSidebar(false,false)})}function filterSummary(paths){var $summary=$(".book-summary");$summary.find("li").each(function(){var path=$(this).data("path");var st=paths==null||_.contains(paths,path);$(this).toggle(st);if(st)$(this).parents("li").show()})}module.exports={init:init,isOpen:isOpen,toggle:toggleSidebar,filter:filterSummary}},{"./platform":17,"./state":19,"./storage":20,jquery:1,lodash:2}],19:[function(require,module,exports){var $=require("jquery");var url=require("url");var path=require("path");var state={};state.update=function(dom){var $book=$(dom.find(".book"));state.$book=$book;state.level=$book.data("level");state.basePath=$book.data("basepath");state.innerLanguage=$book.data("innerlanguage");state.revision=$book.data("revision");state.filepath=$book.data("filepath");state.chapterTitle=$book.data("chapter-title");state.root=url.resolve(location.protocol+"//"+location.host,path.dirname(path.resolve(location.pathname.replace(/\/$/,"/index.html"),state.basePath))).replace(/\/?$/,"/");state.bookRoot=state.innerLanguage?url.resolve(state.root,".."):state.root};state.update($);module.exports=state},{jquery:1,path:4,url:10}],20:[function(require,module,exports){var baseKey="";module.exports={setBaseKey:function(key){baseKey=key},set:function(key,value){key=baseKey+":"+key;try{sessionStorage[key]=JSON.stringify(value)}catch(e){}},get:function(key,def){key=baseKey+":"+key;if(sessionStorage[key]===undefined)return def;try{var v=JSON.parse(sessionStorage[key]);return v==null?def:v}catch(err){return sessionStorage[key]||def}},remove:function(key){key=baseKey+":"+key;sessionStorage.removeItem(key)}}},{}],21:[function(require,module,exports){var $=require("jquery");var _=require("lodash");var events=require("./events");var buttons=[];function insertAt(parent,selector,index,element){var lastIndex=parent.children(selector).length;if(index<0){index=Math.max(0,lastIndex+1+index)}parent.append(element);if(index",{class:"dropdown-menu",html:''});if(_.isString(dropdown)){$menu.append(dropdown)}else{var groups=_.map(dropdown,function(group){if(_.isArray(group))return group;else return[group]});_.each(groups,function(group){var $group=$("
",{class:"buttons"});var sizeClass="size-"+group.length;_.each(group,function(btn){btn=_.defaults(btn||{},{text:"",className:"",onClick:defaultOnClick});var $btn=$("'; - var clipboard; - - gitbook.events.bind("page.change", function() { - - if (!ClipboardJS.isSupported()) return; - - // the page.change event is thrown twice: before and after the page changes - if (clipboard) { - // clipboard is already defined but we are on the same page - if (clipboard._prevPage === window.location.pathname) return; - // clipboard is already defined and url path change - // we can deduct that we are before page changes - clipboard.destroy(); // destroy the previous events listeners - clipboard = undefined; // reset the clipboard object - return; - } - - $(copyButton).prependTo("div.sourceCode"); - - clipboard = new ClipboardJS(".copy-to-clipboard-button", { - text: function(trigger) { - return trigger.parentNode.textContent; - } - }); - - clipboard._prevPage = window.location.pathname - - }); - -}); diff --git a/docs/libs/gitbook-2.6.7/js/plugin-fontsettings.js b/docs/libs/gitbook-2.6.7/js/plugin-fontsettings.js deleted file mode 100644 index a70f0fb374..0000000000 --- a/docs/libs/gitbook-2.6.7/js/plugin-fontsettings.js +++ /dev/null @@ -1,152 +0,0 @@ -gitbook.require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { - var fontState; - - var THEMES = { - "white": 0, - "sepia": 1, - "night": 2 - }; - - var FAMILY = { - "serif": 0, - "sans": 1 - }; - - // Save current font settings - function saveFontSettings() { - gitbook.storage.set("fontState", fontState); - update(); - } - - // Increase font size - function enlargeFontSize(e) { - e.preventDefault(); - if (fontState.size >= 4) return; - - fontState.size++; - saveFontSettings(); - }; - - // Decrease font size - function reduceFontSize(e) { - e.preventDefault(); - if (fontState.size <= 0) return; - - fontState.size--; - saveFontSettings(); - }; - - // Change font family - function changeFontFamily(index, e) { - e.preventDefault(); - - fontState.family = index; - saveFontSettings(); - }; - - // Change type of color - function changeColorTheme(index, e) { - e.preventDefault(); - - var $book = $(".book"); - - if (fontState.theme !== 0) - $book.removeClass("color-theme-"+fontState.theme); - - fontState.theme = index; - if (fontState.theme !== 0) - $book.addClass("color-theme-"+fontState.theme); - - saveFontSettings(); - }; - - function update() { - var $book = gitbook.state.$book; - - $(".font-settings .font-family-list li").removeClass("active"); - $(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active"); - - $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); - $book.addClass("font-size-"+fontState.size); - $book.addClass("font-family-"+fontState.family); - - if(fontState.theme !== 0) { - $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); - $book.addClass("color-theme-"+fontState.theme); - } - }; - - function init(config) { - var $bookBody, $book; - - //Find DOM elements. - $book = gitbook.state.$book; - $bookBody = $book.find(".book-body"); - - // Instantiate font state object - fontState = gitbook.storage.get("fontState", { - size: config.size || 2, - family: FAMILY[config.family || "sans"], - theme: THEMES[config.theme || "white"] - }); - - update(); - }; - - - gitbook.events.bind("start", function(e, config) { - var opts = config.fontsettings; - if (!opts) return; - - // Create buttons in toolbar - gitbook.toolbar.createButton({ - icon: 'fa fa-font', - label: 'Font Settings', - className: 'font-settings', - dropdown: [ - [ - { - text: 'A', - className: 'font-reduce', - onClick: reduceFontSize - }, - { - text: 'A', - className: 'font-enlarge', - onClick: enlargeFontSize - } - ], - [ - { - text: 'Serif', - onClick: _.partial(changeFontFamily, 0) - }, - { - text: 'Sans', - onClick: _.partial(changeFontFamily, 1) - } - ], - [ - { - text: 'White', - onClick: _.partial(changeColorTheme, 0) - }, - { - text: 'Sepia', - onClick: _.partial(changeColorTheme, 1) - }, - { - text: 'Night', - onClick: _.partial(changeColorTheme, 2) - } - ] - ] - }); - - - // Init current settings - init(opts); - }); -}); - - diff --git a/docs/libs/gitbook-2.6.7/js/plugin-search.js b/docs/libs/gitbook-2.6.7/js/plugin-search.js deleted file mode 100644 index 747fccebd0..0000000000 --- a/docs/libs/gitbook-2.6.7/js/plugin-search.js +++ /dev/null @@ -1,270 +0,0 @@ -gitbook.require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { - var index = null; - var fuse = null; - var _search = {engine: 'lunr', opts: {}}; - var $searchInput, $searchLabel, $searchForm; - var $highlighted = [], hi, hiOpts = { className: 'search-highlight' }; - var collapse = false, toc_visible = []; - - function init(config) { - // Instantiate search settings - _search = gitbook.storage.get("search", { - engine: config.search.engine || 'lunr', - opts: config.search.options || {}, - }); - }; - - // Save current search settings - function saveSearchSettings() { - gitbook.storage.set("search", _search); - } - - // Use a specific index - function loadIndex(data) { - // [Yihui] In bookdown, I use a character matrix to store the chapter - // content, and the index is dynamically built on the client side. - // Gitbook prebuilds the index data instead: https://github.com/GitbookIO/plugin-search - // We can certainly do that via R packages V8 and jsonlite, but let's - // see how slow it really is before improving it. On the other hand, - // lunr cannot handle non-English text very well, e.g. the default - // tokenizer cannot deal with Chinese text, so we may want to replace - // lunr with a dumb simple text matching approach. - if (_search.engine === 'lunr') { - index = lunr(function () { - this.ref('url'); - this.field('title', { boost: 10 }); - this.field('body'); - }); - data.map(function(item) { - index.add({ - url: item[0], - title: item[1], - body: item[2] - }); - }); - return; - } - fuse = new Fuse(data.map((_data => { - return { - url: _data[0], - title: _data[1], - body: _data[2] - }; - })), Object.assign( - { - includeScore: true, - threshold: 0.1, - ignoreLocation: true, - keys: ["title", "body"] - }, - _search.opts - )); - } - - // Fetch the search index - function fetchIndex() { - return $.getJSON(gitbook.state.basePath+"/search_index.json") - .then(loadIndex); // [Yihui] we need to use this object later - } - - // Search for a term and return results - function search(q) { - let results = []; - switch (_search.engine) { - case 'fuse': - if (!fuse) return; - results = fuse.search(q).map(function(result) { - var parts = result.item.url.split('#'); - return { - path: parts[0], - hash: parts[1] - }; - }); - break; - case 'lunr': - default: - if (!index) return; - results = _.chain(index.search(q)).map(function(result) { - var parts = result.ref.split("#"); - return { - path: parts[0], - hash: parts[1] - }; - }) - .value(); - } - - // [Yihui] Highlight the search keyword on current page - $highlighted = $('.page-inner') - .unhighlight(hiOpts).highlight(q, hiOpts).find('span.search-highlight'); - scrollToHighlighted(0); - - return results; - } - - // [Yihui] Scroll the chapter body to the i-th highlighted string - function scrollToHighlighted(d) { - var n = $highlighted.length; - hi = hi === undefined ? 0 : hi + d; - // navignate to the previous/next page in the search results if reached the top/bottom - var b = hi < 0; - if (d !== 0 && (b || hi >= n)) { - var path = currentPath(), n2 = toc_visible.length; - if (n2 === 0) return; - for (var i = b ? 0 : n2; (b && i < n2) || (!b && i >= 0); i += b ? 1 : -1) { - if (toc_visible.eq(i).data('path') === path) break; - } - i += b ? -1 : 1; - if (i < 0) i = n2 - 1; - if (i >= n2) i = 0; - var lnk = toc_visible.eq(i).find('a[href$=".html"]'); - if (lnk.length) lnk[0].click(); - return; - } - if (n === 0) return; - var $p = $highlighted.eq(hi); - $p[0].scrollIntoView(); - $highlighted.css('background-color', ''); - // an orange background color on the current item and removed later - $p.css('background-color', 'orange'); - setTimeout(function() { - $p.css('background-color', ''); - }, 2000); - } - - function currentPath() { - var href = window.location.pathname; - href = href.substr(href.lastIndexOf('/') + 1); - return href === '' ? 'index.html' : href; - } - - // Create search form - function createForm(value) { - if ($searchForm) $searchForm.remove(); - if ($searchLabel) $searchLabel.remove(); - if ($searchInput) $searchInput.remove(); - - $searchForm = $('
', { - 'class': 'book-search', - 'role': 'search' - }); - - $searchLabel = $('",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0Traits page. +Generally, variables are defined in ontologies that are predefined. We often use ontologies from cropontology.org. In this case, you will not be able to define your own variables directly; instead, you will need to contact us and we will add the variable for you. We will discuss manual trait or treatment creation later in this section. -If the database you are on allows you to directly add observation variables, you will see the following button at the bottom of the page. +Before setting out to introduce new traits, visit the trait search page under the *Search* tab to make sure it isn't already present. Once a set of traits, methods, units, scales, and other terms have been added to the database, they can be composed into an observation variable from the *Analyze* tab under *Compose a new trait*. ```{r echo=FALSE, out.width='95%', fig.align='center'} -knitr::include_graphics('assets/images/manage_observation_variables_start_button.png') +knitr::include_graphics('assets/images/compose_a_new_trait_tab.png') ``` -When you click the button, the following workflow will appear. You should be logged in or else it will not allow addition of the observation variable. -The workflow begins with an introduction. +A base trait term can be combined with an object/plant part, collection method, measurement unit, and time terms to combine into a new observation variable. The types of combinations available to be made determine which of these can be combined. The types of composed traits available to be made is determined in your server configuration - contact a server administrator with questions. ```{r echo=FALSE, out.width='95%', fig.align='center'} -knitr::include_graphics('assets/images/manage_observation_variables_workflow_intro.png') +knitr::include_graphics('assets/images/compose_a_new_trait_page.png') ``` -On the next workflow step, you select the ontology that you want to insert the new observation variable into. You must also give a name and a definition for the new observation variable. +To proceed, select one or more term from each box. At the bottom of the page, you will see existing composed traits that match your selections as well as the new composed traits that can be made. Select one or more of the possible new composed traits and click 'Submit' to create them. ```{r echo=FALSE, out.width='95%', fig.align='center'} -knitr::include_graphics('assets/images/manage_observation_variables_workflow_observation_variable.png') +knitr::include_graphics('assets/images/compose_a_new_trait_selections.png') ``` -On the next workflow step, you select the trait ontology to use. Once you select a trait ontology, a select containing all the terms in the selected ontology will appear. You can either select a trait or if it does not exist in the select, you can create a new one by giving a name and a definition for the new trait. +Afterwards, you can use the newly created observation variable ontology term in your phenotyping. The same interface can also be used for treatments, but on the *Compose a new treatment* page. -```{r echo=FALSE, out.width='95%', fig.align='center'} -knitr::include_graphics('assets/images/manage_observation_variables_workflow_trait.png') -``` +For databases where the user has greater control, we have an interface to allow curators to add new traits or treatments. -On the next workflow step, you select the method ontology to use. Once you select a method ontology, a select containing all the terms in the selected ontology will appear. You can either select a method or if it does not exist in the select, you can create a new one by giving a name and a definition for the new method. +If the database you are on allows you to directly add observation variables (contact a server administrator to change this), you will see links in the tools section of the user profile page. ```{r echo=FALSE, out.width='95%', fig.align='center'} -knitr::include_graphics('assets/images/manage_observation_variables_workflow_method.png') +knitr::include_graphics('assets/images/trait_designer_links.png') ``` -On the next workflow step, you select the scale ontology to use. Once you select a scale ontology, a select containing all the terms in the selected ontology will appear. You can either select a scale or if it does not exist in the select, you can create a new one by giving a name and a definition for the new scale. You can also define a format, minimum, maximum, categories, and default value for the new scale. +On the trait design page, you will see a form prompting you for the trait name, definition, and optional data like the default value, minimum, maximum, or categories. At the bottom of the page, the ontology browser is duplicated for your convenience. ```{r echo=FALSE, out.width='95%', fig.align='center'} -knitr::include_graphics('assets/images/manage_observation_variables_workflow_scale.png') +knitr::include_graphics('assets/images/trait_design_page.png') ``` -On the last page of the workflow, you confirm the submission. - -```{r echo=FALSE, out.width='95%', fig.align='center'} -knitr::include_graphics('assets/images/manage_observation_variables_workflow_submit.png') -``` +The final input box allows you to choose the parent term for the new term. The trait format dropdown allows you to specify numeric traits, categorical traits, or a new ontology. New ontologies allow you to organize your traits into hierarchies. For example, an 'Agronomic trait' sub-ontology could be made to include all agronomic trait measurements in the same group. Note that once a new term has been designated as numeric or categorical, it cannot have more child terms - it is the end of the hierarchy. -Afterwards, you can use the newly created observation variable ontology term in your phenotyping. +Once a new trait has been submitted, the page will refresh and the trait will be available for use in the composition tool. \ No newline at end of file diff --git a/docs/search.html b/docs/search.html deleted file mode 100644 index c5f8f34991..0000000000 --- a/docs/search.html +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "Search Results" -layout: default -tags: -- no_search -- no_toc ---- -{% include crumbs.html context=page.url %} -
-
-{% include search.html %} diff --git a/js/source/entries/fieldmap.js b/js/source/entries/fieldmap.js index a3aa226dd8..e409b0cb83 100644 --- a/js/source/entries/fieldmap.js +++ b/js/source/entries/fieldmap.js @@ -209,21 +209,23 @@ export function init() { filter_heatmap(observations) { this.heatmap_object = {}; for (let observation of observations) { - let trait_name = observation.observationVariableName; - if (!this.heatmap_object[trait_name]) { - this.heatmap_object[trait_name] = { - [observation.observationUnitDbId]: { + if ( ! isNaN(observation.value)) { + let trait_name = observation.observationVariableName; + if (!this.heatmap_object[trait_name]) { + this.heatmap_object[trait_name] = { + [observation.observationUnitDbId]: { + val: observation.value, + plot_name: observation.observationUnitName, + id: observation.observationDbId, + }, + }; + } else { + this.heatmap_object[trait_name][observation.observationUnitDbId] = { val: observation.value, plot_name: observation.observationUnitName, id: observation.observationDbId, - }, - }; - } else { - this.heatmap_object[trait_name][observation.observationUnitDbId] = { - val: observation.value, - plot_name: observation.observationUnitName, - id: observation.observationDbId, - }; + }; + } } } } @@ -689,7 +691,7 @@ export function init() { btnClick(image_ids); }); jQuery("#hm_edit_plot_information").html( - "Selected Plot Information: " + //"Selected Plot Information: " ); jQuery("#hm_plot_name").html(replace_plot_name); jQuery("#hm_plot_number").html(replace_plot_number); @@ -702,7 +704,7 @@ export function init() { jQuery('#hm_plot_structure_container').hide(); new jQuery.ajax({ - url: '/stock/get_plot_contents/'+replace_plot_id, + url: '/stock/get_child_stocks/'+replace_plot_id, success: function (response) { jQuery("#working_modal").modal("hide"); if (response.error) { @@ -836,18 +838,19 @@ export function init() { let display_layout = false; let structure; for (let key in plot_structure["has"]) { - if (plot_structure["has"][key]["type"] == "subplot") { + console.log(key); + if (plot_structure["has"][key]["type"] === "subplot") { for (let subkey in plot_structure["has"][key]["has"]) { if (plot_structure["has"][key]["has"][subkey]["type"] == "plant") { structure = "plot:subplot:plant"; - if (plot_structure["has"][key]["has"][subkey]["attributes"]?.["row_number"]["value"] > 0) { + if (plot_structure["has"][key]["has"][subkey]["attributes"]?.["row_number"]?.["value"] > 0) { display_layout = true; } } } - } else if (plot_structure["has"][key]["type"] == "plant") { + } else if (plot_structure["has"][key]["type"] === "plant") { structure = "plot:plant"; - if (plot_structure["has"][key]["attributes"]?.["row_number"]["value"] > 0) { + if (plot_structure["has"][key]["attributes"]?.["row_number"]?.["value"] > 0) { display_layout = true; } } else { @@ -857,7 +860,7 @@ export function init() { let hm_plot_structure_data_container = document.getElementById("hm_plot_structure_data_container"); - delete plot_structure["id"]; + delete plot_structure["stock_id"]; delete plot_structure["type"]; delete plot_structure["name"]; diff --git a/js/source/legacy/CXGN/BreedersToolbox/AddTrial.js b/js/source/legacy/CXGN/BreedersToolbox/AddTrial.js index 672358b4e9..cf9b0a3667 100644 --- a/js/source/legacy/CXGN/BreedersToolbox/AddTrial.js +++ b/js/source/legacy/CXGN/BreedersToolbox/AddTrial.js @@ -493,13 +493,25 @@ jQuery(document).ready(function ($) { var html = ''; for (var i=0; i
'; + html = html + '
'; } html = html + '
'; jQuery('#create_trial_with_treatment_additional_treatment').html(html); + apply_treatment_autocomplete(); return false; }); + apply_treatment_autocomplete(); + + function apply_treatment_autocomplete() { + jQuery('.treatment-name-input-box').each( function() { + jQuery(this).autocomplete({ + source : '/ajax/cvterm/autocompleteslim' + "?db_name=_TREATMENT", + appendTo : '#add_project_dialog' + }); + }); + } + var num_plants_per_plot = 0; var num_subplots_per_plot = 0; @@ -618,7 +630,7 @@ jQuery(document).ready(function ($) { replicated_stock_list = JSON.stringify(list.getList(replicated_stock_list_id)); } - var treatments = [] + var treatments = {}; if (design_type == 'splitplot'){ var count = jQuery('#create_trial_with_treatment_additional_count').val(); if (count == 0) { @@ -626,17 +638,31 @@ jQuery(document).ready(function ($) { } var int_count = parseInt(count); for(var i=1; i<=int_count; i++){ - var treatment_value = jQuery('#create_trial_with_treatment_name_input'+i).val(); - if(treatment_value != ''){ - treatments.push(treatment_value); + var treatment_name = jQuery('#create_trial_with_treatment_name_input'+i).val(); + var treatment_value = jQuery('#create_trial_with_treatment_value_input'+i).val(); + if(treatment_name != '' && treatment_value != ''){ + if (treatment_name in treatments) { + treatments[treatment_name].push(treatment_value); + } else { + treatments[treatment_name] = []; + treatments[treatment_name].push(treatment_value); + } } } var num_plants_per_treatment = $('#num_plants_per_treatment').val(); num_plants_per_plot = 0; + var aggregator = 1; + for (treatment in treatments) { + var num_levels = Object.keys(treatments[treatment]).length + aggregator = aggregator * num_levels; + } + num_subplots_per_plot = aggregator; if (num_plants_per_treatment){ - num_plants_per_plot = num_plants_per_treatment*treatments.length; + num_plants_per_plot = num_plants_per_treatment*num_subplots_per_plot; + } else { + alert("You must supply the number of plants per treatment."); + return; } - num_subplots_per_plot = treatments.length; } var greenhouse_num_plants = []; @@ -666,7 +692,7 @@ jQuery(document).ready(function ($) { use_same_layout = ""; } - var plot_numbering_scheme = $('input[name="plot_numbering_scheme"]:checked').val(); + var plot_numbering_scheme = $('input[name="plot_numbering_scheme"]:checked').val(); $.ajax({ type: 'POST', @@ -702,7 +728,7 @@ jQuery(document).ready(function ($) { 'fieldmap_col_number': fieldmap_col_number, 'fieldmap_row_number': fieldmap_row_number, 'plot_layout_format': plot_layout_format, - 'treatments':treatments, + 'treatments': JSON.stringify(treatments), 'num_plants_per_plot':num_plants_per_plot, 'row_in_design_number': row_in_design_number, 'col_in_design_number': col_in_design_number, @@ -1029,7 +1055,7 @@ jQuery(document).ready(function ($) { } else if (design_method == "greenhouse") { jQuery('#create_trial_design_description_div').html('

A greenhouse/nursery houses plants in no particular layout design. The plants can be of named accessions or in the case of seedling nurseries from crosses, the plants can be of named crosses. Creates plot entities with plant entities in the database.

'); } else if (design_method == "splitplot") { - jQuery('#create_trial_design_description_div').html('

Split plot designs are useful for applying treatments to subplots of a plot. If you give three treatments, there will be three subplots with the treatment(s) distributed randomly among them. Creates plot entities with subplot entities with plant entities in the database.

'); + jQuery('#create_trial_design_description_div').html('

Split plot designs are useful for applying treatments to subplots of a plot. If you give two treatments with two levels each, there will be four subplots with the four treatment combinations distributed randomly among them. Creates plot entities with subplot entities with plant entities in the database.

'); } else if (design_method == "p-rep") { jQuery('#create_trial_design_description_div').html('

Has some treatments that are unreplicated and relies on replicated treatments to make the trial analysable. It is recommended that at least 20% of the experimental units are occupied by replicated treatments. Creates plot entities in the database.

'); } else if (design_method == "Westcott") { @@ -2213,6 +2239,7 @@ jQuery(document).ready(function ($) { } function save_experimental_design(design_json) { + var list = new CXGN.List(); var name = jQuery('#new_trial_name').val(); var year = jQuery('#add_project_year').val(); diff --git a/js/source/legacy/CXGN/BreedersToolbox/UploadPhenotype.js b/js/source/legacy/CXGN/BreedersToolbox/UploadPhenotype.js index 96e9cc2c44..c7a54b5f35 100644 --- a/js/source/legacy/CXGN/BreedersToolbox/UploadPhenotype.js +++ b/js/source/legacy/CXGN/BreedersToolbox/UploadPhenotype.js @@ -1,13 +1,15 @@ jQuery( document ).ready( function() { - function verifyData(formSelector, fileSelector, url, upload_type) { - jQuery("#upload_spreadsheet_phenotype_submit_store").attr('disabled', true); - jQuery('#upload_phenotype_spreadsheet_verify_status').html(""); - jQuery("#upload_datacollector_phenotype_submit_store").attr('disabled', true); - jQuery("#upload_phenotype_datacollector_verify_status").html(""); - jQuery("#upload_fieldbook_phenotype_submit_store").attr('disabled', true); - jQuery("#upload_phenotype_fieldbook_verify_status").html(""); + function verifyData(formSelector, fileSelector, url, upload_type, div_type) { + jQuery(`#upload_spreadsheet_${div_type}_submit_store`).attr('disabled', true); + jQuery(`#upload_${div_type}_spreadsheet_verify_status`).html(""); + if (div_type !== 'treatment') { + jQuery(`#upload_datacollector_${div_type}_submit_store`).attr('disabled', true); + jQuery(`#upload_${div_type}_datacollector_verify_status`).html(""); + jQuery(`#upload_fieldbook_${div_type}_submit_store`).attr('disabled', true); + jQuery(`#upload_${div_type}_fieldbook_verify_status`).html(""); + } let file = jQuery(fileSelector).val(); if ( !file || file === '' ) { @@ -24,16 +26,16 @@ jQuery( document ).ready( function() { timeout: 0, success: function(response) { hidePhenotypeUploadWorkingModal(); - displayPhenotypeUploadVerifyResponse(response, upload_type); + displayPhenotypeUploadVerifyResponse(response, upload_type, div_type); }, - error: function() { + error: function(response) { hidePhenotypeUploadWorkingModal(); alert("An error occurred while trying to verify this file. Please check the formatting and try again"); } }); } - function storeData(formSelector, fileSelector, url, upload_type) { + function storeData(formSelector, fileSelector, url, upload_type, div_type) { let file = jQuery(fileSelector).val(); if ( !file || file === '' ) { return alert("Please select a file"); @@ -49,9 +51,9 @@ jQuery( document ).ready( function() { timeout: 0, success: function(response) { hidePhenotypeUploadWorkingModal(); - displayPhenotypeUploadStoreResponse(response, upload_type); + displayPhenotypeUploadStoreResponse(response, upload_type, div_type); }, - error: function() { + error: function(response) { hidePhenotypeUploadWorkingModal(); alert("An error occurred while trying to store this file. Please check the formatting and try again"); } @@ -64,7 +66,8 @@ jQuery( document ).ready( function() { "#upload_spreadsheet_phenotype_file_form", "#upload_spreadsheet_phenotype_file_input", "/ajax/phenotype/upload_verify/spreadsheet", - "spreadsheet" + "spreadsheet", + "phenotype" ); }); @@ -73,7 +76,28 @@ jQuery( document ).ready( function() { "#upload_spreadsheet_phenotype_file_form", "#upload_spreadsheet_phenotype_file_input", "/ajax/phenotype/upload_store/spreadsheet", - "spreadsheet" + "spreadsheet", + "phenotype" + ); + }); + + jQuery('#upload_spreadsheet_treatment_submit_verify').click(function() { + verifyData( + "#upload_spreadsheet_treatment_file_form", + "#upload_spreadsheet_treatment_file_input", + "/ajax/phenotype/upload_verify/spreadsheet/treatment", + "spreadsheet", + "treatment" + ); + }); + + jQuery("#upload_spreadsheet_treatment_submit_store").click(function() { + storeData( + "#upload_spreadsheet_treatment_file_form", + "#upload_spreadsheet_treatment_file_input", + "/ajax/phenotype/upload_store/spreadsheet/treatment", + "spreadsheet", + "treatment" ); }); @@ -83,7 +107,8 @@ jQuery( document ).ready( function() { "#upload_datacollector_phenotype_file_form", "#upload_datacollector_phenotype_file_input", "/ajax/phenotype/upload_verify/datacollector", - "datacollector" + "datacollector", + "phenotype" ); }); @@ -92,7 +117,8 @@ jQuery( document ).ready( function() { "#upload_datacollector_phenotype_file_form", "#upload_datacollector_phenotype_file_input", "/ajax/phenotype/upload_store/datacollector", - "datacollector" + "datacollector", + "phenotype" ); }); @@ -102,7 +128,8 @@ jQuery( document ).ready( function() { "#upload_fieldbook_phenotype_file_form", "#upload_fieldbook_phenotype_file_input", "/ajax/phenotype/upload_verify/fieldbook", - "fieldbook" + "fieldbook", + "phenotype" ); }); @@ -111,29 +138,38 @@ jQuery( document ).ready( function() { "#upload_fieldbook_phenotype_file_form", "#upload_fieldbook_phenotype_file_input", "/ajax/phenotype/upload_store/fieldbook", - "fieldbook" + "fieldbook", + "phenotype" ); }); - const handlePhenotypeFileFormatChange = function() { - var val = jQuery('#upload_spreadsheet_phenotype_file_format').val(); + const handlePhenotypeFileFormatChange = function(div_type) { + var val = jQuery(`#upload_spreadsheet_${div_type}_file_format`).val(); if (val == 'simple') { - jQuery('#upload_spreadsheet_phenotype_data_level_div').hide(); - jQuery('#upload_phenotype_spreadsheet_info').show(); + jQuery(`#upload_spreadsheet_${div_type}_data_level_div`).hide(); + jQuery(`#upload_${div_type}_spreadsheet_info`).show(); } else { - jQuery('#upload_spreadsheet_phenotype_data_level_div').show(); - jQuery('#upload_phenotype_spreadsheet_info').hide(); + jQuery(`#upload_spreadsheet_${div_type}_data_level_div`).show(); + jQuery(`#upload_${div_type}_spreadsheet_info`).hide(); } } - jQuery('#upload_spreadsheet_phenotype_file_format').change(handlePhenotypeFileFormatChange); - handlePhenotypeFileFormatChange(); + jQuery(`#upload_spreadsheet_phenotype_file_format`).change( function () { + handlePhenotypeFileFormatChange('phenotype') + }); + jQuery(`#upload_spreadsheet_treatment_file_format`).change( function () { + handlePhenotypeFileFormatChange('treatment') + }); jQuery('#delete_pheno_file_link').click(function() { alert('Deleted successfully.'); }); - jQuery('#reset_dialog').click( function() { - reset_dialog(jQuery('#upload_spreadsheet_phenotype_file_format').val()); + jQuery('#phenotype_reset_dialog').click( function() { + reset_dialog(jQuery('#upload_spreadsheet_phenotype_file_format').val(), 'phenotype'); + }); + + jQuery('#treatment_reset_dialog').click( function() { + reset_dialog(jQuery('#upload_spreadsheet_treatment_file_format').val(), 'treatment'); }); }); @@ -149,21 +185,22 @@ function hidePhenotypeUploadWorkingModal() { } -function displayPhenotypeUploadVerifyResponse(response, upload_type) { +function displayPhenotypeUploadVerifyResponse(response, upload_type, div_type) { + let submit_store_button, submit_verify_button, upload_phenotype_status; if (upload_type == "spreadsheet") { - var submit_verify_button = "#upload_spreadsheet_phenotype_submit_verify"; - var submit_store_button = "#upload_spreadsheet_phenotype_submit_store"; - var upload_phenotype_status = "#upload_phenotype_spreadsheet_verify_status"; + submit_verify_button = `#upload_spreadsheet_${div_type}_submit_verify`; + submit_store_button = `#upload_spreadsheet_${div_type}_submit_store`; + upload_phenotype_status = `#upload_${div_type}_spreadsheet_verify_status`; } else if (upload_type == "datacollector") { - var submit_verify_button = "#upload_datacollector_phenotype_submit_verify"; - var submit_store_button = "#upload_datacollector_phenotype_submit_store"; - var upload_phenotype_status = "#upload_phenotype_datacollector_verify_status"; + submit_verify_button = `#upload_datacollector_${div_type}_submit_verify`; + submit_store_button = `#upload_datacollector_${div_type}_submit_store`; + upload_phenotype_status = `#upload_${div_type}_datacollector_verify_status`; } else if (upload_type == "fieldbook") { - var submit_verify_button = "#upload_fieldbook_phenotype_submit_verify"; - var submit_store_button = "#upload_fieldbook_phenotype_submit_store"; - var upload_phenotype_status = "#upload_phenotype_fieldbook_verify_status"; + submit_verify_button = `#upload_fieldbook_${div_type}_submit_verify`; + submit_store_button = `#upload_fieldbook_${div_type}_submit_store`; + upload_phenotype_status = `#upload_${div_type}_fieldbook_verify_status`; } jQuery(submit_verify_button).attr('disabled', true); @@ -196,11 +233,11 @@ function displayPhenotypeUploadVerifyResponse(response, upload_type) { message_text += "
  • "; message_text += ""; message_text += "Warnings are shown in yellow. Either fix the file and try again or continue with storing the data."; - message_text += "
    To overwrite previously stored values instead: "; - message_text += ""; - message_text += "
    New values will be uploaded. Any previously stored values will be skipped.
    "; - message_text += ""; - message_text += ""; + message_text += `
    To overwrite previously stored values instead: `; + message_text += ``; + message_text += `
    New values will be uploaded. Any previously stored values will be skipped.
    `; + message_text += ``; + message_text += ``; message_text += "
    "; message_text += "
  • "; for (var i = 0; i < warningarrayLength; i++) { @@ -213,32 +250,33 @@ function displayPhenotypeUploadVerifyResponse(response, upload_type) { } message_text += ""; jQuery(upload_phenotype_status).html(message_text); - jQuery('#phenotype_upload_overwrite_values').off('change').on('change', function() { - jQuery("#phenotype_upload_remove_values_div").css('display', this.checked ? 'block' : 'none'); + jQuery(`#${div_type}_upload_overwrite_values`).off('change').on('change', function() { + jQuery(`#${div_type}_upload_remove_values_div`).css('display', this.checked ? 'block' : 'none'); updateDetails(); }); - jQuery('#phenotype_upload_remove_values').off('change').on('change', updateDetails); + jQuery(`#${div_type}_upload_remove_values`).off('change').on('change', updateDetails); function updateDetails() { - const overwrite = jQuery('#phenotype_upload_overwrite_values').is(':checked'); - const remove = jQuery('#phenotype_upload_remove_values').is(':checked'); - jQuery('#phenotype_upload_details_default').css('display', !overwrite && !remove ? 'block' : 'none'); - jQuery('#phenotype_upload_details_overwrite').css('display', overwrite && !remove ? 'block' : 'none'); - jQuery('#phenotype_upload_details_remove').css('display', overwrite && remove ? 'block' : 'none'); + const overwrite = jQuery(`#${div_type}_upload_overwrite_values`).is(':checked'); + const remove = jQuery(`#${div_type}_upload_remove_values`).is(':checked'); + jQuery(`#${div_type}_upload_details_default`).css('display', !overwrite && !remove ? 'block' : 'none'); + jQuery(`#${div_type}_upload_details_overwrite`).css('display', overwrite && !remove ? 'block' : 'none'); + jQuery(`#${div_type}_upload_details_remove`).css('display', overwrite && remove ? 'block' : 'none'); } } -function displayPhenotypeUploadStoreResponse(response, upload_type) { +function displayPhenotypeUploadStoreResponse(response, upload_type, div_type) { + let submit_store_button, upload_phenotype_status; if (upload_type == "spreadsheet") { - var submit_store_button = "#upload_spreadsheet_phenotype_submit_store"; - var upload_phenotype_status = "#upload_phenotype_spreadsheet_verify_status"; + submit_store_button = `#upload_spreadsheet_${div_type}_submit_store`; + upload_phenotype_status = `#upload_${div_type}_spreadsheet_verify_status`; } else if (upload_type == "datacollector") { - var submit_store_button = "#upload_datacollector_phenotype_submit_store"; - var upload_phenotype_status = "#upload_phenotype_datacollector_verify_status"; + submit_store_button = `#upload_datacollector_${div_type}_submit_store`; + upload_phenotype_status = `#upload_${div_type}_datacollector_verify_status`; } else if (upload_type == "fieldbook") { - var submit_store_button = "#upload_fieldbook_phenotype_submit_store"; - var upload_phenotype_status = "#upload_phenotype_fieldbook_verify_status"; + submit_store_button = `#upload_fieldbook_${div_type}_submit_store`; + upload_phenotype_status = `#upload_${div_type}_fieldbook_verify_status`; } jQuery(upload_phenotype_status).empty(); @@ -262,28 +300,28 @@ function displayPhenotypeUploadStoreResponse(response, upload_type) { message_text += ""; } if (errorarrayLength == 0) { - message_text += "

  • Upload Successfull!

  • "; + message_text += "

  • Upload Successful!

  • "; } } else { - message_text += "

  • Upload Successfull!

  • "; + message_text += "

  • Upload Successful!

  • "; } message_text += ""; jQuery(upload_phenotype_status).html(message_text); } -function reset_dialog(upload_type) { - jQuery("#upload_spreadsheet_phenotype_file_input").val(""); - jQuery("#upload_spreadsheet_phenotype_submit_verify").attr('disabled', false); - jQuery("#upload_spreadsheet_phenotype_submit_store").attr('disabled', true); - jQuery('#upload_phenotype_spreadsheet_verify_status').html(""); +function reset_dialog(upload_type, div_type) { + jQuery(`#upload_spreadsheet_${div_type}_file_input`).val(""); + jQuery(`#upload_spreadsheet_${div_type}_submit_verify`).attr('disabled', false); + jQuery(`#upload_spreadsheet_${div_type}_submit_store`).attr('disabled', true); + jQuery(`#upload_${div_type}_spreadsheet_verify_status`).html(""); - jQuery("#upload_datacollector_phenotype_file_input").val(""); - jQuery("#upload_datacollector_phenotype_submit_verify").attr('disabled', false); - jQuery("#upload_datacollector_phenotype_submit_store").attr('disabled', true); - jQuery("#upload_phenotype_datacollector_verify_status").html(""); + jQuery(`#upload_datacollector_${div_type}_file_input`).val(""); + jQuery(`#upload_datacollector_${div_type}_submit_verify`).attr('disabled', false); + jQuery(`#upload_datacollector_${div_type}_submit_store`).attr('disabled', true); + jQuery(`#upload_${div_type}_datacollector_verify_status`).html(""); - jQuery("#upload_fieldbook_phenotype_file_input").val(""); - jQuery("#upload_fieldbook_phenotype_submit_verify").attr('disabled', false); - jQuery("#upload_fieldbook_phenotype_submit_store").attr('disabled', true); - jQuery("#upload_phenotype_fieldbook_verify_status").html(""); + jQuery(`#upload_fieldbook_${div_type}_file_input`).val(""); + jQuery(`#upload_fieldbook_${div_type}_submit_verify`).attr('disabled', false); + jQuery(`#upload_fieldbook_${div_type}_submit_store`).attr('disabled', true); + jQuery(`#upload_${div_type}_fieldbook_verify_status`).html(""); } diff --git a/js/source/legacy/CXGN/BreedersToolbox/UploadTrial.js b/js/source/legacy/CXGN/BreedersToolbox/UploadTrial.js index 0c57724596..1a0bcfc37e 100644 --- a/js/source/legacy/CXGN/BreedersToolbox/UploadTrial.js +++ b/js/source/legacy/CXGN/BreedersToolbox/UploadTrial.js @@ -220,6 +220,7 @@ jQuery(document).ready(function ($) { return; } if (response.success) { + add_plants_per_plot(); refreshTrailJsTree(0); jQuery("#upload_trial_error_display_second_try").hide(); jQuery('#trial_upload_show_repeat_upload_button').hide(); @@ -229,7 +230,6 @@ jQuery(document).ready(function ($) { Workflow.skip('#upload_trial_error_display_second_try', false); Workflow.focus("#trial_upload_workflow", -1); //Go to success page Workflow.check_complete("#trial_upload_workflow"); - add_plants_per_plot(); } }, error: function() { diff --git a/js/source/legacy/CXGN/ComposeTrait.js b/js/source/legacy/CXGN/ComposeTrait.js index 14bf54237d..908f1fb471 100644 --- a/js/source/legacy/CXGN/ComposeTrait.js +++ b/js/source/legacy/CXGN/ComposeTrait.js @@ -37,6 +37,7 @@ function get_component_ids () { if (jQuery("#method_select").val()) { component_ids.push(jQuery("#method_select").val()); } if (jQuery("#unit_select").val()) { component_ids.push(jQuery("#unit_select").val()); } if (jQuery("#trait_select").val()) { component_ids.push(jQuery("#trait_select").val()); } + if (jQuery("#experiment_treatment_select").val()) { component_ids.push(jQuery("#experiment_treatment_select").val()); } if (jQuery("#tod_select").val()) { component_ids.push(jQuery("#tod_select").val()); } if (jQuery("#toy_select").val()) { component_ids.push(jQuery("#toy_select").val()); } if (jQuery("#gen_select").val()) { component_ids.push(jQuery("#gen_select").val()); } @@ -54,7 +55,11 @@ function retrieve_matching_traits (component_ids) { ids["attribute_ids"] = jQuery("#attribute_select").val(); ids["method_ids"] = jQuery("#method_select").val(); ids["unit_ids"] = jQuery("#unit_select").val(); - ids["trait_ids"] = jQuery("#trait_select").val(); + if (jQuery("#trait_select").val()) { + ids["trait_ids"] = jQuery("#trait_select").val(); + } else { + ids["trait_ids"] = jQuery("#experiment_treatment_select").val(); + } ids["tod_ids"] = jQuery("#tod_select").val(); ids["toy_ids"] = jQuery("#toy_select").val(); ids["gen_ids"] = jQuery("#gen_select").val(); diff --git a/js/source/legacy/tools/LabelDesigner.js b/js/source/legacy/tools/LabelDesigner.js index 38a2b54e2c..82b62618dd 100644 --- a/js/source/legacy/tools/LabelDesigner.js +++ b/js/source/legacy/tools/LabelDesigner.js @@ -1389,7 +1389,16 @@ function addToLabel(field, text, type, size, font, x, y, width, height) { "text-anchor": text_alignment, //middle "alignment-baseline": "middle", }) - .text(text) + .selectAll("tspan") + .data(text.split("\n")) + .enter() + .append("tspan") + .attr("x", 0) + .attr("type", type) + .attr("value", field) + .attr("dy", (d, i) => i === 0 ? "0em" : "1.2em") // line spacing + .text(d => d); + //console.log("Field is: "+field+" and size is: "+size+" and type is: "+type+" and font size is: "+font_size+" and font is: "+font+" and style is: "+font_styles[font]+" and text is: "+text); break; @@ -1546,23 +1555,33 @@ function checkIfVisible(element) { } function getLabelDetails(element) { - var transform_attributes = parseTransform(element.parentNode.getAttribute('transform')); // return transform attributes as an object - //console.log("Transform attributes are: "+JSON.stringify(transform_attributes)); + var transform_attributes = parseTransform(element.parentNode.getAttribute('transform')); var coords = transform_attributes.translate; - var scale = transform_attributes.scale || new Array(1,1); + var scale = transform_attributes.scale || [1, 1]; var rect = element.getBBox(); var width = rect.width * scale[0]; var height = rect.height * scale[1]; - //console.log("Height is: "+height+" and width is: "+width); var type = element.getAttribute("type"); - var x; - var y; + var x, y; + if (type.match(/Text/)) { - x = parseInt(coords[0]) - y = parseInt(coords[1]) + x = parseInt(coords[0]); + y = parseInt(coords[1]); + } else { + x = parseInt(coords[0]) + (width / 2); + y = parseInt(coords[1]) + (height / 2); + } + + let value = ""; + if (type.match(/Text/)) { + const tspans = element.querySelectorAll("tspan"); + if (tspans.length > 1) { + value = Array.from(tspans).map(t => t.textContent).join("\n"); + } else { + value = element.getAttribute("value"); + } } else { - x = parseInt(coords[0]) + (width/2); - y = parseInt(coords[1]) + (height/2); + value = element.getAttribute("value"); } return { @@ -1570,8 +1589,8 @@ function getLabelDetails(element) { y: y, height: height, width: width, - value: element.getAttribute("value"), - type: element.getAttribute("type"), + value: value, + type: type, font: element.getAttribute("font") || 'Courier', size: element.getAttribute("size") }; diff --git a/lib/CXGN/Analysis/AnalysisCreate.pm b/lib/CXGN/Analysis/AnalysisCreate.pm index e3afd527f1..9137f18d87 100644 --- a/lib/CXGN/Analysis/AnalysisCreate.pm +++ b/lib/CXGN/Analysis/AnalysisCreate.pm @@ -480,9 +480,6 @@ sub store { } my @composed_trait_names = values %composed_trait_map; - - - #Project BUILD inserts project entry my $a = CXGN::Analysis->new({ bcs_schema => $bcs_schema, diff --git a/lib/CXGN/BrAPI/v1/ObservationVariables.pm b/lib/CXGN/BrAPI/v1/ObservationVariables.pm index 44148b3918..e6fbd097b4 100644 --- a/lib/CXGN/BrAPI/v1/ObservationVariables.pm +++ b/lib/CXGN/BrAPI/v1/ObservationVariables.pm @@ -70,7 +70,7 @@ sub observation_variable_ontologies { my $self = shift; my $inputs = shift; my $name_spaces = $inputs->{name_spaces} || []; - my $cvprop_types = $inputs->{cvprop_type_names} || ['trait_ontology','method_ontology','unit_ontology','composed_trait_ontology','object_ontology','attribute_ontology','time_ontology']; + my $cvprop_types = $inputs->{cvprop_type_names} || ['trait_ontology','method_ontology','unit_ontology','composed_trait_ontology','object_ontology','attribute_ontology','time_ontology', 'experiment_treatment_ontology', 'composed_experiment_treatment_ontology']; my $page_size = $self->page_size; my $page = $self->page; my $status = $self->status; diff --git a/lib/CXGN/BrAPI/v2/ObservationUnits.pm b/lib/CXGN/BrAPI/v2/ObservationUnits.pm index d7658845b3..8368d4f4ad 100644 --- a/lib/CXGN/BrAPI/v2/ObservationUnits.pm +++ b/lib/CXGN/BrAPI/v2/ObservationUnits.pm @@ -147,20 +147,20 @@ sub _search { } ## Formatting treatments - my @brapi_treatments; - - if ($c->config->{brapi_treatments_no_management_factor}) { - my $treatments = $obs_unit->{treatments}; - foreach my $treatment (@$treatments) { - while (my ($factor, $modality) = each %$treatment) { - my $modality = $modality ? $modality : undef; - push @brapi_treatments, { - factor => $factor, - modality => $modality, - }; - } - } - } + # my @brapi_treatments; + + # if ($c->config->{brapi_treatments_no_management_factor}) { + # my $treatments = $obs_unit->{treatments}; + # foreach my $treatment (@$treatments) { + # while (my ($factor, $modality) = each %$treatment) { + # my $modality = $modality ? $modality : undef; + # push @brapi_treatments, { + # factor => $factor, + # modality => $modality, + # }; + # } + # } + # } my %numbers; @@ -305,7 +305,7 @@ sub _search { studyDbId => qq|$obs_unit->{trial_id}|, studyName => $obs_unit->{trial_name}, plotImageDbIds => $obs_unit->{image_ids}, - treatments => \@brapi_treatments, + #treatments => \@brapi_treatments, trialDbId => $obs_unit->{folder_id} ? qq|$obs_unit->{folder_id}| : qq|$obs_unit->{trial_id}|, trialName => $obs_unit->{folder_name} ? $obs_unit->{folder_name} : $obs_unit->{trial_name}, }; @@ -394,7 +394,7 @@ sub observationunits_update { my $observationUnit_position_arrayref = $params->{observationUnitPosition} ? $params->{observationUnitPosition} : undef; my $observationUnit_x_ref = $params->{externalReferences} ? $params->{externalReferences} : undef; my $seedlot_id = $params->{seedLotDbId} || ""; #not implemented yet - my $treatments = $params->{treatments} || ""; #not implemented yet + #my $treatments = $params->{treatments} || ""; #not implemented yet my $row_number = $params->{observationUnitPosition}->{positionCoordinateY} ? $params->{observationUnitPosition}->{positionCoordinateY} : undef; my $col_number = $params->{observationUnitPosition}->{positionCoordinateX} ? $params->{observationUnitPosition}->{positionCoordinateX} : undef; @@ -804,8 +804,8 @@ sub observationunits_store { nd_geolocation_id => $location_id, # nd_experiment_id => $nd_experiment->nd_experiment_id(), #optional is_genotyping => 0, - new_treatment_has_plant_entries => 0, - new_treatment_has_subplot_entries => 0, + #new_treatment_has_plant_entries => 0, + #new_treatment_has_subplot_entries => 0, operator => $user_name, trial_stock_type => 'accessions', design_type => $design_type, diff --git a/lib/CXGN/BrAPI/v2/Observations.pm b/lib/CXGN/BrAPI/v2/Observations.pm index ed97927baa..29af323e31 100644 --- a/lib/CXGN/BrAPI/v2/Observations.pm +++ b/lib/CXGN/BrAPI/v2/Observations.pm @@ -28,7 +28,7 @@ sub search { my $limit; my $brapi_study_ids_arrayref = $params->{studyDbId} || ($params->{studyDbIds} || ()); if (!$brapi_study_ids_arrayref || scalar (@$brapi_study_ids_arrayref) < 1) { $limit=1000000; } # if no ids, limit should be set to max and retrieve whole database. If ids no limit to retrieves all - + #TODO: figure out why this has no brapi study Id arrayref ($data,$counter) = _search($self,$params,$limit); my %result = (data=>$data); diff --git a/lib/CXGN/File/Parse.pm b/lib/CXGN/File/Parse.pm index 12c4f13af6..0d7767bfff 100644 --- a/lib/CXGN/File/Parse.pm +++ b/lib/CXGN/File/Parse.pm @@ -171,8 +171,7 @@ When both required_columns and optional_columns are included in the constructor, - an array of the column headers in the file that are neither required nor optional For example, the trial upload template can specify the required columns (such as trial_name, plot_number, etc), -and the optional columns (such as planting_date, harvest_date, etc). Any of the 'additional_columns' will -be treated as treatments. +and the optional columns (such as planting_date, harvest_date, etc). Any of the 'additional_columns' will result in a warning. =head1 PLUGINS diff --git a/lib/CXGN/List/Validate/Plugin/Traits.pm b/lib/CXGN/List/Validate/Plugin/Traits.pm index fdcf3fa515..893d982253 100644 --- a/lib/CXGN/List/Validate/Plugin/Traits.pm +++ b/lib/CXGN/List/Validate/Plugin/Traits.pm @@ -59,7 +59,7 @@ sub validate { } else { $query->{'dbxref.accession'} = $accession; } - if ( $db_name eq 'COMP' && $validator->{composable_validation_check_name} ) { + if ( ($db_name eq 'COMP' || $db_name eq 'COMP_EXP_TREATMENT') && $validator->{composable_validation_check_name} ) { $query->{'me.name'} = $trait_name; } my $rs = $schema->resultset("Cv::Cvterm")->search($query, {'join' => 'dbxref'}); diff --git a/lib/CXGN/Onto.pm b/lib/CXGN/Onto.pm index 747760f5f2..5b13cf37db 100644 --- a/lib/CXGN/Onto.pm +++ b/lib/CXGN/Onto.pm @@ -8,6 +8,7 @@ use Try::Tiny; use Bio::Chado::Schema; use SGN::Model::Cvterm; use Sort::Naturally; +use List::Util qw/max/; has 'schema' => ( isa => 'Bio::Chado::Schema', @@ -134,6 +135,7 @@ sub get_root_nodes { sub store_composed_term { my $self = shift; my $new_trait_names = shift; + my $type = shift || 'trait'; #print STDERR Dumper $new_trait_names; my $schema = $self->schema(); @@ -165,25 +167,59 @@ sub store_composed_term { next; } - my $db = $schema->resultset("General::Db")->find_or_create({ name => 'COMP' }); - my $cv= $schema->resultset('Cv::Cv')->find_or_create( { name => 'composed_trait' }); + my $db; + my $cv; + my $root_term_name; + my $accession; + my $dbname; - my $accession_query = "SELECT nextval('composed_trait_ids')"; - my $h = $dbh->prepare($accession_query); - $h->execute(); - my $accession = $h->fetchrow_array(); + my $h; + + if ($type eq 'trait') { + $db = $schema->resultset("General::Db")->find_or_create({ name => 'COMP' }); + $dbname = 'COMP'; + $cv= $schema->resultset('Cv::Cv')->find_or_create( { name => 'composed_trait' }); + $root_term_name = 'Composed traits'; + + my $accession_query = "SELECT nextval('composed_trait_ids')"; + $h = $dbh->prepare($accession_query); + $h->execute(); + $accession = $h->fetchrow_array(); + } elsif ($type eq 'experiment_treatment') { + $db = $schema->resultset("General::Db")->find_or_create({ name => 'COMP_EXP_TREATMENT' }); + $dbname = 'COMP_EXP_TREATMENT'; + $cv= $schema->resultset('Cv::Cv')->find_or_create( { name => 'composed_experiment_treatment' }); + $root_term_name = 'Composed experimental treatment ontology'; + + my $get_db_accessions_sql = "SELECT accession FROM dbxref JOIN db USING (db_id) WHERE db.name='COMP_EXP_TREATMENT';"; + + $h = $schema->storage->dbh->prepare($get_db_accessions_sql); + $h->execute(); + + my @accessions; + + while (my $accession = $h->fetchrow_array()) { + push @accessions, int($accession =~ s/^0+//r); + } + + if (scalar(@accessions) > 0) { + $accession = max(@accessions) + 1; + } else { + $accession = 1; + } + } my $new_term_dbxref = $schema->resultset("General::Dbxref")->create( { db_id => $db->get_column('db_id'), accession => sprintf("%07d",$accession) }); - #parent term for post-composed traits should already be in teh database. + #parent term for post-composed traits should already be in the database. #Using here create_with if for some reason the root term for the COMP ontology needs to be created my $parent_term= $schema->resultset("Cv::Cvterm")->create_with( { cv =>$cv, - name => 'Composed traits', - db => $db, + name => $root_term_name, + db => $db }); print STDERR "Parent cvterm_id = " . $parent_term->cvterm_id(); @@ -226,7 +262,7 @@ sub store_composed_term { } } - push @new_terms, [$new_term->cvterm_id, $new_term->name().'|COMP:'.sprintf("%07d",$accession)]; + push @new_terms, [$new_term->cvterm_id, $new_term->name()."|$dbname:".sprintf("%07d",$accession)]; } #Takes long on cassavabase.. instead the materialized view is refreshed automatically in a background ajax process. diff --git a/lib/CXGN/Phenotypes/StorePhenotypes.pm b/lib/CXGN/Phenotypes/StorePhenotypes.pm index e2bda74c28..9f22aab567 100644 --- a/lib/CXGN/Phenotypes/StorePhenotypes.pm +++ b/lib/CXGN/Phenotypes/StorePhenotypes.pm @@ -657,7 +657,7 @@ sub check_measurement { #check that trait value is valid for trait name (but only if we have a trait_value) if ((defined($trait_value) && $trait_value ne '' && $trait_value ne 'NA' && $trait_value ne '.') && exists($self->check_trait_format()->{$trait_cvterm_id})) { # print STDERR "Trait minimum value checks if it exists: " . $self->check_trait_min_value->{$trait_cvterm_id} . "\n"; - if ($self->check_trait_format()->{$trait_cvterm_id} eq 'numeric') { + if ($self->check_trait_format()->{$trait_cvterm_id} =~ m/numeric|counter|percent|boolean/i ) { my $trait_format_checked = looks_like_number($trait_value); if (!$trait_format_checked && $trait_value ne '') { $error_message .= "This trait value should be numeric:
    Plot Name: ".$plot_name."
    Trait Name: ".$trait_name."
    Value: ".$trait_value."

    "; @@ -705,8 +705,12 @@ sub check_measurement { } } } else { - # Catch everything else ## not sure what this is for - %trait_categories_hash = map { $_ => 1 } @trait_categories; + if (grep { m/=/ } @trait_categories) { #different style of categories where each value is 0=value/1=value/... + %trait_categories_hash = map { $_ =~ m/(\d+)=/ => $_ =~ m/=(.*)/} @trait_categories; + } else { + # Catch everything else ## not sure what this is for + %trait_categories_hash = map { $_ => 1 } @trait_categories; + } } # print STDERR "TRAIT CATEGORIES: ".Dumper(\%trait_categories_hash)."\n"; @@ -714,7 +718,9 @@ sub check_measurement { my @check_values; if (exists($self->check_trait_category()->{$trait_cvterm_id}) && - $self->check_trait_format->{$trait_cvterm_id} eq 'Multicat') { + ($self->check_trait_format->{$trait_cvterm_id} eq 'Multicat' || + $self->check_trait_format->{$trait_cvterm_id} eq 'categorical' || + $self->check_trait_format->{$trait_cvterm_id} eq 'qualitative')) { # print STDERR "Dealing with a categorical trait!\n\n"; diff --git a/lib/CXGN/PhenotypingTrial.pm b/lib/CXGN/PhenotypingTrial.pm index 8f0df13ec2..5284e9608b 100644 --- a/lib/CXGN/PhenotypingTrial.pm +++ b/lib/CXGN/PhenotypingTrial.pm @@ -230,7 +230,37 @@ sub get_stock_entry_summary { =cut -sub remove_treatment { +sub remove_treatment { #TODO REFACTOR + my $self = shift; + my $schema = $self->bcs_schema; + my $treatment_id =shift; + + my $trial_id = $self->get_trial_id(); + + my $treatment_rs = $schema->resultset('Project::Project')->find({ project_id => $treatment_id }); + if (!$treatment_rs) { + return { error => "Treatment not found" }; + } + + eval { + $treatment_rs->delete(); + }; + + return { success => 1 }; +} + +=head2 remove_treatment_project + + Usage: my $trial_object->remove_treatment_project($treatment_id); + Desc: removes the selected treatment from this trial + Ret: + Args: $treatment_id + Side Effects: + Example: + +=cut + +sub remove_treatment_project { my $self = shift; my $schema = $self->bcs_schema; my $treatment_id =shift; @@ -316,12 +346,12 @@ sub get_crossing_experiments_from_field_trial { Desc: add additional accessions or crosses or families for existing greenhouse trial Ret: Args: - Side Effects: + Side Effects: Treatments are NOT added automatically when calling this, since this adds new plots/accessions. Users will have to add treatments to the new stocks afterwards. Example: =cut -sub add_additional_stocks_for_greenhouse { +sub add_additional_stocks_for_greenhouse { my $self = shift; my $schema = $self->bcs_schema; my $stock_list = shift; @@ -440,9 +470,11 @@ sub add_additional_stocks_for_greenhouse { sub add_additional_plants_for_greenhouse { my $self = shift; my $schema = $self->bcs_schema; + my $metadata_schema = $self->metadata_schema; my $stock_list = shift; my $number_of_plants_list = shift; my $user_id = shift; + my $phenotype_store_config = shift; my $trial_id = $self->get_trial_id(); my $add_additional_plants = '1'; @@ -500,9 +532,9 @@ sub add_additional_plants_for_greenhouse { } } - my $greenhouse_trial = CXGN::Trial->new( { bcs_schema => $schema, trial_id => $trial_id }); + my $greenhouse_trial = CXGN::Trial->new( { bcs_schema => $schema, trial_id => $trial_id, metadata_schema => $metadata_schema }); - $greenhouse_trial->save_plant_entries($info,'' ,'' ,$user_id, $add_additional_plants); + $greenhouse_trial->save_plant_entries($info,'' ,1 ,$user_id, $phenotype_store_config, $add_additional_plants); #TODO: Add phenotypestore config hash my $new_layout = CXGN::Trial::TrialLayout->new({ schema => $schema, diff --git a/lib/CXGN/Project.pm b/lib/CXGN/Project.pm index 580431f265..fd443c9ba6 100644 --- a/lib/CXGN/Project.pm +++ b/lib/CXGN/Project.pm @@ -29,6 +29,7 @@ use Moose; use Data::Dumper; use Try::Tiny; +use List::MoreUtils qw(uniq); use CXGN::Trial::Folder; use CXGN::Stock; use CXGN::Trial::TrialLayout; @@ -39,9 +40,13 @@ use Time::Seconds; use CXGN::Calendar; use JSON; use File::Basename qw | basename dirname|; +use File::Temp 'tempfile'; use Scalar::Util qw | looks_like_number |; use CXGN::Genotype::GenotypingProject; use CXGN::Genotype::Protocol; +use CXGN::Phenotypes::StorePhenotypes; +use CXGN::Phenotypes::SearchFactory; +use CXGN::People::Person; =head2 accessor bcs_schema() @@ -2549,10 +2554,10 @@ sub _delete_field_layout_experiment { return { success => 1 }; } -sub _delete_management_factors_experiments { +sub _delete_management_factors_experiments { # DEPRECATED my $self = shift; my $management_factor_type_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'treatment_experiment', 'experiment_type')->cvterm_id(); - my $management_factors = $self->get_treatments; + my $management_factors = $self->get_treatment_projects; #$self->get_treatments foreach (@$management_factors){ my $m = CXGN::Trial->new({ bcs_schema => $self->bcs_schema, @@ -2572,6 +2577,78 @@ sub _delete_management_factors_experiments { return { success => 1 }; } +sub add_management_factor { + my $self = shift; + my $management_factor = shift; + my $schema = $self->bcs_schema(); + my $management_regime_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'management_regime', 'project_property')->cvterm_id(); + my $mf_rs = $schema->resultset("Project::Projectprop")->find({ + project_id => $self->get_trial_id(), + type_id => $management_regime_type_id + }); + + if ($mf_rs) { + my $management_regime = decode_json($mf_rs->value()); + push @{$management_regime}, $management_factor; + $management_regime = encode_json($management_regime); + $mf_rs->update({ + value => $management_regime + }); + } else { + $schema->resultset("Project::Projectprop")->create({ + project_id => $self->get_trial_id(), + value => encode_json([$management_factor]), + type_id => $management_regime_type_id + }); + } +} + +sub get_management_regime { + my $self = shift; + my $schema = $self->bcs_schema(); + my $management_regime_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'management_regime', 'project_property')->cvterm_id(); + my $mf_rs = $schema->resultset("Project::Projectprop")->find({ + project_id => $self->get_trial_id(), + type_id => $management_regime_type_id + }); + + if ($mf_rs) { + my $management_regime = decode_json($mf_rs->value()); + return $management_regime; + } else { + return ""; + } +} + +sub remove_management_factor { + my $self = shift; + my $management_factor = shift; + my $schema = $self->bcs_schema(); + my $management_regime_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'management_regime', 'project_property')->cvterm_id(); + my $mf_rs = $schema->resultset("Project::Projectprop")->find({ + project_id => $self->get_trial_id(), + type_id => $management_regime_type_id + }); + + if ($mf_rs) { + my $management_regime = decode_json($mf_rs->value()); + $management_regime = [grep { + !($_->{type} eq $management_factor->{type} && + $_->{schedule} eq $management_factor->{schedule} && + $_->{description} eq $management_factor->{description} && + encode_json($_->{completions}) eq encode_json($management_factor->{completions}) && + $_->{start_date} eq $management_factor->{start_date} && + $_->{end_date} eq $management_factor->{end_date}) + } @{$management_regime}]; + $management_regime = encode_json($management_regime); + $mf_rs->update({ + value => $management_regime + }); + } else { + die "No management regime to edit."; + } +} + =head2 function delete_project_entry() Usage: @@ -2857,7 +2934,7 @@ sub get_phenotypes_for_trait { sub get_stock_phenotypes_for_traits { my $self = shift; my $trait_ids = shift; - my $stock_type = shift; #plot, plant, all + my $stock_type = shift; #plot, plant, subplot, all my $stock_relationships = shift; #arrayref. plot_of, plant_of my $relationship_stock_type = shift; #plot, plant my $subject_or_object = shift; @@ -3194,7 +3271,7 @@ sub get_project_start_date_cvterm_id { =cut -sub create_plant_entities { +sub create_plant_entities { my $self = shift; my $plants_per_plot = shift || 30; my $inherits_plot_treatments = shift; @@ -3202,9 +3279,27 @@ sub create_plant_entities { my $plant_owner_username = shift; my $rows_per_plot = shift; my $cols_per_plot = shift; + my $phenotype_store_config = shift; + + my $chado_schema = $self->bcs_schema(); + + my %phenostore_stocks = (); + my $treatments = $self->get_treatments(); + my $phenostore_data_hash = {}; + my $phenosearch = CXGN::Phenotypes::SearchFactory->instantiate( + 'Native', { + bcs_schema => $chado_schema, + trait_list => [map {$_->{trait_id}} @{$treatments}], + trial_list => [$self->get_trial_id()], + data_level => 'plot' + }); + my $treatment_data = $phenosearch->search(); + my $plot_pheno = {}; + foreach my $row (@{$treatment_data}) { + $plot_pheno->{$row->{obsunit_uniquename}}->{$row->{trait_name}} = $row->{phenotype_value}; + } my $create_plant_entities_txn = sub { - my $chado_schema = $self->bcs_schema(); my $layout = CXGN::Trial::TrialLayout->new( { schema => $chado_schema, trial_id => $self->get_trial_id(), experiment_type=>'field_layout' }); my $design = $layout->get_design(); @@ -3223,34 +3318,9 @@ sub create_plant_entities { my $col_num_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'col_number', 'stock_property')->cvterm_id(); my $has_plants_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_plant_entries', 'project_property')->cvterm_id(); my $field_layout_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'field_layout', 'experiment_type')->cvterm_id(); - my $treatment_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id(); # my $row_num_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'row_number', 'stock_property')->cvterm_id(); #my $plants_per_plot_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'plants_per_plot', 'project_property')->cvterm_id(); - my $treatments; - my %treatment_experiments; - my %treatment_plots; - if ($inherits_plot_treatments){ - $treatments = $self->get_treatments(); - foreach (@$treatments){ - - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ - type_id => $has_plants_cvterm, - value => $plants_per_plot, - project_id => $_->[0], - }); - - my $treatment_nd_experiment = $chado_schema->resultset("Project::Project")->search( { 'me.project_id' => $_->[0] }, {select=>['nd_experiment.nd_experiment_id']})->search_related('nd_experiment_projects')->search_related('nd_experiment', { 'nd_experiment.type_id' => $treatment_cvterm })->single(); - $treatment_experiments{$_->[0]} = $treatment_nd_experiment->nd_experiment_id(); - - my $treatment_trial = CXGN::Project->new({ bcs_schema => $chado_schema, trial_id => $_->[0]}); - my $plots = $treatment_trial->get_plots(); - foreach my $plot (@$plots){ - $treatment_plots{$_->[0]}->{$plot->[0]} = 1; - } - } - } - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ type_id => $has_plants_cvterm, value => $plants_per_plot, @@ -3274,7 +3344,7 @@ sub create_plant_entities { if (! $plot_row) { print STDERR "The plot $plot is not found in the database\n"; - return "The plot $plot is not yet in the database. Cannot create plant entries."; + die "The plot $plot is not yet in the database. Cannot create plant entries."; } my $parent_plot = $plot_row->stock_id(); @@ -3284,6 +3354,21 @@ sub create_plant_entities { foreach my $plant_index_number (1..$plants_per_plot) { my $plant_name = $parent_plot_name."_plant_$plant_index_number"; #print STDERR "... ... creating plant $plant_name...\n"; + + foreach my $treatment (@{$treatments}) { + my $treatment_name = $treatment->{trait_name}; + if (exists($plot_pheno->{$parent_plot_name}->{$treatment_name})) { + $phenostore_data_hash->{$plant_name}->{$treatment_name} = [ + $plot_pheno->{$parent_plot_name}->{$treatment_name}, + $phenotype_store_config->{metadata_hash}->{date}, + $phenotype_store_config->{metadata_hash}->{operator}, + '', + '' + ]; + $phenostore_stocks{$plant_name} = 1; + } + } + my $row_num; my $col_num; @@ -3294,8 +3379,8 @@ sub create_plant_entities { $self->_save_plant_entry($chado_schema, $accession_cvterm, $cross_cvterm, $family_name_cvterm, $parent_plot_organism, $parent_plot_name, $parent_plot, $plant_name, $plant_cvterm, $plant_index_number, $plant_index_number_cvterm, $block_cvterm, $plot_number_cvterm, - $replicate_cvterm, $row_num_cvterm, $row_num, $col_num_cvterm, $col_num, $plant_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, $inherits_plot_treatments, $treatments, - $plot_relationship_cvterm, \%treatment_plots, \%treatment_experiments, $treatment_cvterm, $plant_owner, $plant_owner_username); + $replicate_cvterm, $row_num_cvterm, $row_num, $col_num_cvterm, $col_num, $plant_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, + $plot_relationship_cvterm, $plant_owner, $plant_owner_username); } } @@ -3304,6 +3389,42 @@ sub create_plant_entities { eval { $self->bcs_schema()->txn_do($create_plant_entities_txn); + + if ($inherits_plot_treatments && $treatments) { + my @treatment_names = map {$_->{trait_name}} @{$treatments}; + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $phenotype_store_config->{basepath}, + dbhost => $phenotype_store_config->{dbhost}, + dbname => $phenotype_store_config->{dbname}, + dbuser => $phenotype_store_config->{dbuser}, + dbpass => $phenotype_store_config->{dbpass}, + temp_file_nd_experiment_id => $phenotype_store_config->{temp_file_nd_experiment_id}, + bcs_schema => $chado_schema, + metadata_schema => $self->metadata_schema, + phenome_schema => $self->phenome_schema, + user_id => $phenotype_store_config->{user_id}, + stock_list => [keys(%phenostore_stocks)], + trait_list => \@treatment_names, + values_hash => $phenostore_data_hash, + metadata_hash => $phenotype_store_config->{metadata_hash} + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + die $verified_error; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + die "An error occurred inheriting treatments: $stored_phenotype_error\n"; + } + } }; if ($@) { print STDERR "An error occurred creating the plant entities. $@\n"; @@ -3316,7 +3437,7 @@ sub create_plant_entities { =head2 function save_plant_entries() - Usage: $trial->save_plant_entries(\%data, $plants_per_plot, $inherits_plot_treatments); + Usage: $trial->save_plant_entries(\%data, $plants_per_plot, $inherits_plot_treatments, $owner_id, $phenotype_store_config); Desc: Some trials require plant-level data. It is possible to upload plant_names to save. Ret: @@ -3334,10 +3455,28 @@ sub save_plant_entries { my $plants_per_plot = shift; my $inherits_plot_treatments = shift; my $user_id = shift; + my $phenotype_store_config = shift; my $add_additional_plants = shift; + my $chado_schema = $self->bcs_schema(); + + my %phenostore_stocks = (); + my $treatments = $self->get_treatments(); + my $phenostore_data_hash = {}; + my $phenosearch = CXGN::Phenotypes::SearchFactory->instantiate( + 'Native', { + bcs_schema => $chado_schema, + trait_list => [map {$_->{trait_id}} @{$treatments}], + trial_list => [$self->get_trial_id()], + data_level => 'plot' + }); + my $treatment_data = $phenosearch->search(); + my $plot_pheno = {}; + foreach my $row (@{$treatment_data}) { + $plot_pheno->{$row->{obsunit_uniquename}}->{$row->{trait_name}} = $row->{phenotype_value}; + } + my $create_plant_entities_txn = sub { - my $chado_schema = $self->bcs_schema(); my $layout = CXGN::Trial::TrialLayout->new( { schema => $chado_schema, trial_id => $self->get_trial_id(), experiment_type=>'field_layout' }); my $design = $layout->get_design(); @@ -3356,32 +3495,13 @@ sub save_plant_entries { my $col_num_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'col_number', 'stock_property')->cvterm_id(); my $has_plants_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_plant_entries', 'project_property')->cvterm_id(); my $field_layout_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'field_layout', 'experiment_type')->cvterm_id(); - my $treatment_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id(); #my $plants_per_plot_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'plants_per_plot', 'project_property')->cvterm_id(); - my $treatments; - my %treatment_experiments; - my %treatment_plots; - if ($inherits_plot_treatments){ - $treatments = $self->get_treatments(); - foreach (@$treatments){ - - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ - type_id => $has_plants_cvterm, - value => $plants_per_plot, - project_id => $_->[0], - }); - - my $treatment_nd_experiment = $chado_schema->resultset("Project::Project")->search( { 'me.project_id' => $_->[0] }, {select=>['nd_experiment.nd_experiment_id']})->search_related('nd_experiment_projects')->search_related('nd_experiment', { type_id => $treatment_cvterm })->single(); - $treatment_experiments{$_->[0]} = $treatment_nd_experiment->nd_experiment_id(); - - my $treatment_trial = CXGN::Trial->new({ bcs_schema => $chado_schema, trial_id => $_->[0]}); - my $plots = $treatment_trial->get_plots(); - foreach my $plot (@$plots){ - $treatment_plots{$_->[0]}->{$plot->[0]} = 1; - } - } - } + # my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ + # type_id => $has_plants_cvterm, + # value => $plants_per_plot, + # project_id => $self->get_trial_id(), + # }); if (!$add_additional_plants) { my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ @@ -3401,7 +3521,7 @@ sub save_plant_entries { if (!$plot_row) { print STDERR "The plot $plot_name is not found in the database\n"; - return "The plot $plot_name is not yet in the database. Cannot create plant entries."; + die "The plot $plot_name is not yet in the database. Cannot create plant entries."; } my $parent_plot = $plot_row->stock_id(); @@ -3413,6 +3533,19 @@ sub save_plant_entries { my $plant_index_numbers = $val->{plant_index_numbers}; my $increment = 0; foreach my $plant_name (@$plant_names) { + foreach my $treatment (@{$treatments}) { + my $treatment_name = $treatment->{trait_name}; + if (exists($plot_pheno->{$parent_plot_name}->{$treatment_name})) { + $phenostore_data_hash->{$plant_name}->{$treatment_name} = [ + $plot_pheno->{$parent_plot_name}->{$treatment_name}, + $phenotype_store_config->{metadata_hash}->{date}, + $phenotype_store_config->{metadata_hash}->{operator}, + '', + '' + ]; + $phenostore_stocks{$plant_name} = 1; + } + } my $given_plant_index_number = $plant_index_numbers->[$increment]; my $plant_index_number_save = $given_plant_index_number ? $given_plant_index_number : $plant_index_number; @@ -3425,7 +3558,7 @@ sub save_plant_entries { $self->_save_plant_entry($chado_schema, $accession_cvterm, $cross_cvterm, $family_name_cvterm, $parent_plot_organism, $parent_plot_name, $parent_plot, $plant_name, $plant_cvterm, $plant_index_number_save, $plant_index_number_cvterm, $block_cvterm, $plot_number_cvterm, $replicate_cvterm, $row_num_cvterm, $row_num, $col_num_cvterm, $col_num, $plant_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, - $inherits_plot_treatments, $treatments, $plot_relationship_cvterm, \%treatment_plots, \%treatment_experiments, $treatment_cvterm); + $plot_relationship_cvterm, $user_id); $plant_index_number++; $increment++; } @@ -3436,6 +3569,43 @@ sub save_plant_entries { eval { $self->bcs_schema()->txn_do($create_plant_entities_txn); + + if ($inherits_plot_treatments && $treatments) { + my @treatment_names = map {$_->{trait_name}} @{$treatments}; + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $phenotype_store_config->{basepath}, + dbhost => $phenotype_store_config->{dbhost}, + dbname => $phenotype_store_config->{dbname}, + dbuser => $phenotype_store_config->{dbuser}, + dbpass => $phenotype_store_config->{dbpass}, + temp_file_nd_experiment_id => $phenotype_store_config->{temp_file_nd_experiment_id}, + bcs_schema => $chado_schema, + metadata_schema => $self->metadata_schema, + phenome_schema => $self->phenome_schema, + user_id => $phenotype_store_config->{user_id}, + stock_list => [keys(%phenostore_stocks)], + trait_list => \@treatment_names, + values_hash => $phenostore_data_hash, + metadata_hash => $phenotype_store_config->{metadata_hash} + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + die $verified_error; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + die "An error occurred inheriting treatments: $stored_phenotype_error\n"; + } + } + }; if ($@) { print STDERR "An error occurred creating the plant entities. $@\n"; @@ -3466,9 +3636,27 @@ sub create_plant_subplot_entities { my $plant_owner_username = shift; my $rows_per_subplot = shift; my $cols_per_subplot = shift; + my $phenotype_store_config = shift; + + my $chado_schema = $self->bcs_schema(); + + my %phenostore_stocks = (); + my $treatments = $self->get_treatments(); + my $phenostore_data_hash = {}; + my $phenosearch = CXGN::Phenotypes::SearchFactory->instantiate( + 'Native', { + bcs_schema => $chado_schema, + trait_list => [map {$_->{trait_id}} @{$treatments}], + trial_list => [$self->get_trial_id()], + data_level => 'subplot' + }); + my $treatment_data = $phenosearch->search(); + my $subplot_pheno = {}; + foreach my $row (@{$treatment_data}) { + $subplot_pheno->{$row->{obsunit_uniquename}}->{$row->{trait_name}} = $row->{phenotype_value}; + } my $create_plant_entities_txn = sub { - my $chado_schema = $self->bcs_schema(); my $layout = CXGN::Trial::TrialLayout->new( { schema => $chado_schema, trial_id => $self->get_trial_id(), experiment_type=>'field_layout' }); my $design = $layout->get_design(); @@ -3490,7 +3678,6 @@ sub create_plant_subplot_entities { my $has_subplots_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_subplot_entries', 'project_property')->cvterm_id(); my $has_plants_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_plant_entries', 'project_property')->cvterm_id(); my $field_layout_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'field_layout', 'experiment_type')->cvterm_id(); - my $treatment_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id(); #my $plants_per_plot_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'plants_per_plot', 'project_property')->cvterm_id(); # Calculate the number of plants per plot (subplots_per_plot * plants_per_subplot) @@ -3501,30 +3688,6 @@ sub create_plant_subplot_entities { my $subplots_per_plot = $subplots_per_plot_row->value(); my $plants_per_plot = $subplots_per_plot * $plants_per_subplot; - my $treatments; - my %treatment_experiments; - my %treatment_plots; - if ($inherits_plot_treatments){ - $treatments = $self->get_treatments(); - foreach (@$treatments){ - - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ - type_id => $has_plants_cvterm, - value => $plants_per_plot, - project_id => $_->[0], - }); - - my $treatment_nd_experiment = $chado_schema->resultset("Project::Project")->search( { 'me.project_id' => $_->[0] }, {select=>['nd_experiment.nd_experiment_id']})->search_related('nd_experiment_projects')->search_related('nd_experiment', { 'nd_experiment.type_id' => $treatment_cvterm })->single(); - $treatment_experiments{$_->[0]} = $treatment_nd_experiment->nd_experiment_id(); - - my $treatment_trial = CXGN::Project->new({ bcs_schema => $chado_schema, trial_id => $_->[0]}); - my $plots = $treatment_trial->get_plots(); - foreach my $plot (@$plots){ - $treatment_plots{$_->[0]}->{$plot->[0]} = 1; - } - } - } - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ type_id => $has_plants_cvterm, value => $plants_per_plot, @@ -3540,7 +3703,7 @@ sub create_plant_subplot_entities { if (! $plot_row) { print STDERR "The plot $plot is not found in the database\n"; - return "The plot $plot is not yet in the database. Cannot create plant entries."; + die "The plot $plot is not yet in the database. Cannot create plant entries."; } my $parent_plot = $plot_row->stock_id(); @@ -3554,7 +3717,7 @@ sub create_plant_subplot_entities { my $subplot_row = $chado_schema->resultset("Stock::Stock")->find({ uniquename => $subplot, type_id => $subplot_cvterm }); if ( !$subplot_row ) { print STDERR "The subplot $subplot is not found in the database\n"; - return "The subplot $subplot is not yet in the database. Cannot create plant entries."; + die "The subplot $subplot is not yet in the database. Cannot create plant entries."; } my $parent_subplot = $subplot_row->stock_id(); @@ -3572,6 +3735,19 @@ sub create_plant_subplot_entities { foreach my $plant_index_number (1..$plants_per_subplot) { my $plant_name = $subplot."_plant_$plant_index_number"; # print STDERR "... ... ... creating plant $plant_name...\n"; + foreach my $treatment (@{$treatments}) { + my $treatment_name = $treatment->{trait_name}; + if (exists($subplot_pheno->{$subplot}->{$treatment_name})) { + $phenostore_data_hash->{$plant_name}->{$treatment_name} = [ + $subplot_pheno->{$subplot}->{$treatment_name}, + $phenotype_store_config->{metadata_hash}->{date}, + $phenotype_store_config->{metadata_hash}->{operator}, + '', + '' + ]; + $phenostore_stocks{$plant_name} = 1; + } + } my $row_num; my $col_num; @@ -3583,8 +3759,8 @@ sub create_plant_subplot_entities { $self->_save_plant_entry($chado_schema, $accession_cvterm, $cross_cvterm, $family_name_cvterm, $parent_plot_organism, $parent_plot_name, $parent_plot, $plant_name, $plant_cvterm, $plant_index_number, $plant_index_number_cvterm, $block_cvterm, $plot_number_cvterm, - $replicate_cvterm, $row_num_cvterm, $row_num, $col_num_cvterm, $col_num, $plant_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, $inherits_plot_treatments, $treatments, - $plot_relationship_cvterm, \%treatment_plots, \%treatment_experiments, $treatment_cvterm, $plant_owner, $plant_owner_username, + $replicate_cvterm, $row_num_cvterm, $row_num, $col_num_cvterm, $col_num, $plant_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, + $plot_relationship_cvterm, $plant_owner, $plant_owner_username, $parent_subplot, $plant_subplot_relationship_cvterm); } } @@ -3595,6 +3771,42 @@ sub create_plant_subplot_entities { eval { $self->bcs_schema()->txn_do($create_plant_entities_txn); + + if ($inherits_plot_treatments && $treatments) { + my @treatment_names = map {$_->{trait_name}} @{$treatments}; + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $phenotype_store_config->{basepath}, + dbhost => $phenotype_store_config->{dbhost}, + dbname => $phenotype_store_config->{dbname}, + dbuser => $phenotype_store_config->{dbuser}, + dbpass => $phenotype_store_config->{dbpass}, + temp_file_nd_experiment_id => $phenotype_store_config->{temp_file_nd_experiment_id}, + bcs_schema => $chado_schema, + metadata_schema => $self->metadata_schema, + phenome_schema => $self->phenome_schema, + user_id => $phenotype_store_config->{user_id}, + stock_list => [keys(%phenostore_stocks)], + trait_list => \@treatment_names, + values_hash => $phenostore_data_hash, + metadata_hash => $phenotype_store_config->{metadata_hash} + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + die $verified_error; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + die "An error occurred inheriting treatments: $stored_phenotype_error\n"; + } + } }; if ($@) { print STDERR "An error occurred creating the plant entities. $@\n"; @@ -3627,9 +3839,27 @@ sub save_plant_subplot_entries { my $inherits_plot_treatments = shift; my $plant_owner = shift; my $plant_owner_username = shift; + my $phenotype_store_config = shift; + + my $chado_schema = $self->bcs_schema(); + + my %phenostore_stocks = (); + my $treatments = $self->get_treatments(); + my $phenostore_data_hash = {}; + my $phenosearch = CXGN::Phenotypes::SearchFactory->instantiate( + 'Native', { + bcs_schema => $chado_schema, + trait_list => [map {$_->{trait_id}} @{$treatments}], + trial_list => [$self->get_trial_id()], + data_level => 'subplot' + }); + my $treatment_data = $phenosearch->search(); + my $subplot_pheno = {}; + foreach my $row (@{$treatment_data}) { + $subplot_pheno->{$row->{obsunit_uniquename}}->{$row->{trait_name}} = $row->{phenotype_value}; + } my $create_plant_entities_txn = sub { - my $chado_schema = $self->bcs_schema(); my $layout = CXGN::Trial::TrialLayout->new( { schema => $chado_schema, trial_id => $self->get_trial_id(), experiment_type=>'field_layout' }); my $design = $layout->get_design(); @@ -3652,7 +3882,6 @@ sub save_plant_subplot_entries { my $has_subplots_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_subplot_entries', 'project_property')->cvterm_id(); my $has_plants_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_plant_entries', 'project_property')->cvterm_id(); my $field_layout_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'field_layout', 'experiment_type')->cvterm_id(); - my $treatment_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id(); # Calculate the number of plants per plot (subplots_per_plot * plants_per_subplot) my $subplots_per_plot_row = $chado_schema->resultset("Project::Projectprop")->find({ @@ -3662,30 +3891,6 @@ sub save_plant_subplot_entries { my $subplots_per_plot = $subplots_per_plot_row->value(); my $plants_per_plot = $subplots_per_plot * $plants_per_subplot; - my $treatments; - my %treatment_experiments; - my %treatment_plots; - if ($inherits_plot_treatments){ - $treatments = $self->get_treatments(); - foreach (@$treatments){ - - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ - type_id => $has_plants_cvterm, - value => $plants_per_plot, - project_id => $_->[0], - }); - - my $treatment_nd_experiment = $chado_schema->resultset("Project::Project")->search( { 'me.project_id' => $_->[0] }, {select=>['nd_experiment.nd_experiment_id']})->search_related('nd_experiment_projects')->search_related('nd_experiment', { 'nd_experiment.type_id' => $treatment_cvterm })->single(); - $treatment_experiments{$_->[0]} = $treatment_nd_experiment->nd_experiment_id(); - - my $treatment_trial = CXGN::Trial->new({ bcs_schema => $chado_schema, trial_id => $_->[0]}); - my $plots = $treatment_trial->get_plots(); - foreach my $plot (@$plots){ - $treatment_plots{$_->[0]}->{$plot->[0]} = 1; - } - } - } - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ type_id => $has_plants_cvterm, value => $plants_per_plot, @@ -3703,7 +3908,7 @@ sub save_plant_subplot_entries { my $subplot_row = $chado_schema->resultset("Stock::Stock")->find( { stock_id=>$subplot_stock_id }); if (!$subplot_row) { print STDERR "The subplot $subplot_name is not found in the database\n"; - return "The subplot $subplot_name is not yet in the database. Cannot create plant entries."; + die "The subplot $subplot_name is not yet in the database. Cannot create plant entries."; } my $plot_relationship_row = $chado_schema->resultset("Stock::StockRelationship")->find({ @@ -3712,12 +3917,12 @@ sub save_plant_subplot_entries { }); if (!$plot_relationship_row) { print STDERR "The subplot $subplot_name does not have a defined plot relationship in the database\n"; - return "The subplot $subplot_name does not have a defined plot relationship in the database. Cannot create plant entries."; + die "The subplot $subplot_name does not have a defined plot relationship in the database. Cannot create plant entries."; } my $plot_row = $chado_schema->resultset("Stock::Stock")->find({ stock_id => $plot_relationship_row->subject_id() }); if (!$plot_row) { print STDERR "The parent plot of subplot $subplot_name is not found in the database\n"; - return "The parent plot of subplot $subplot_name is not yet in the database. Cannot create plant entries."; + die "The parent plot of subplot $subplot_name is not yet in the database. Cannot create plant entries."; } my $parent_plot = $plot_row->stock_id(); @@ -3728,10 +3933,25 @@ sub save_plant_subplot_entries { my $plant_names = $val->{plant_names}; my $plant_index_numbers = $val->{plant_index_numbers}; my $increment = 0; + foreach my $plant_name (@$plant_names) { my $given_plant_index_number = $plant_index_numbers->[$increment]; my $plant_index_number_save = $given_plant_index_number ? $given_plant_index_number : $plant_index_number; + foreach my $treatment (@{$treatments}) { + my $treatment_name = $treatment->{trait_name}; + if (exists($subplot_pheno->{$subplot_name}->{$treatment_name})) { + $phenostore_data_hash->{$plant_name}->{$treatment_name} = [ + $subplot_pheno->{$subplot_name}->{$treatment_name}, + $phenotype_store_config->{metadata_hash}->{date}, + $phenotype_store_config->{metadata_hash}->{operator}, + '', + '' + ]; + $phenostore_stocks{$plant_name} = 1; + } + } + my ($row_num, $col_num); if ($val->{plant_coords}) { my $coord_pair = shift(@{$val->{plant_coords}}); @@ -3741,8 +3961,8 @@ sub save_plant_subplot_entries { $self->_save_plant_entry($chado_schema, $accession_cvterm, $cross_cvterm, $family_name_cvterm, $parent_plot_organism, $parent_plot_name, $parent_plot, $plant_name, $plant_cvterm, $plant_index_number_save, $plant_index_number_cvterm, $block_cvterm, $plot_number_cvterm, $replicate_cvterm, $row_num_cvterm, $row_num, $col_num_cvterm, $col_num, $plant_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, - $inherits_plot_treatments, $treatments, $plot_relationship_cvterm, - \%treatment_plots, \%treatment_experiments, $treatment_cvterm, $plant_owner, $plant_owner_username, + $plot_relationship_cvterm, + $plant_owner, $plant_owner_username, $subplot_stock_id, $plant_subplot_relationship_cvterm); $plant_index_number++; $increment++; @@ -3753,7 +3973,44 @@ sub save_plant_subplot_entries { }; eval { + $self->bcs_schema()->txn_do($create_plant_entities_txn); + + if ($inherits_plot_treatments && $treatments) { + my @treatment_names = map {$_->{trait_name}} @{$treatments}; + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ #not right stock list... + basepath => $phenotype_store_config->{basepath}, + dbhost => $phenotype_store_config->{dbhost}, + dbname => $phenotype_store_config->{dbname}, + dbuser => $phenotype_store_config->{dbuser}, + dbpass => $phenotype_store_config->{dbpass}, + temp_file_nd_experiment_id => $phenotype_store_config->{temp_file_nd_experiment_id}, + bcs_schema => $chado_schema, + metadata_schema => $self->metadata_schema, + phenome_schema => $self->phenome_schema, + user_id => $phenotype_store_config->{user_id}, + stock_list => [keys(%phenostore_stocks)], + trait_list => \@treatment_names, + values_hash => $phenostore_data_hash, + metadata_hash => $phenotype_store_config->{metadata_hash} + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + die $verified_error; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + die "An error occurred inheriting treatments: $stored_phenotype_error\n"; + } + } }; if ($@) { print STDERR "An error occurred creating the plant entities. $@\n"; @@ -3787,18 +4044,11 @@ sub _save_plant_entry { my $plant_relationship_cvterm = shift; my $field_layout_experiment = shift; my $field_layout_cvterm = shift; - my $inherits_plot_treatments = shift; - my $treatments = shift; my $plot_relationship_cvterm = shift; - my $treatment_plots_ref = shift; - my $treatment_experiments_ref = shift; - my $treatment_cvterm = shift; my $plant_owner = shift; my $plant_owner_username = shift; my $parent_subplot = shift; my $plant_subplot_relationship_cvterm = shift; - my %treatment_plots = %$treatment_plots_ref; - my %treatment_experiments = %$treatment_experiments_ref; my $plant = $chado_schema->resultset("Stock::Stock")->create({ organism_id => $parent_plot_organism, @@ -3876,21 +4126,6 @@ sub _save_plant_entry { type_id => $field_layout_cvterm, stock_id => $plant->stock_id(), }); - - if ($inherits_plot_treatments){ - if($treatments){ - foreach (@$treatments){ - my $plots = $treatment_plots{$_->[0]}; - if (exists($plots->{$parent_plot})){ - my $plant_nd_experiment_stock = $chado_schema->resultset("NaturalDiversity::NdExperimentStock")->create({ - nd_experiment_id => $treatment_experiments{$_->[0]}, - type_id => $treatment_cvterm, - stock_id => $plant->stock_id(), - }); - } - } - } - } } =head2 function has_plant_entries() @@ -3941,10 +4176,27 @@ sub create_tissue_samples { my $use_tissue_numbers = shift; my $tissue_sample_owner = shift; my $username = shift; + my $phenotype_store_config = shift; + my $chado_schema = $self->bcs_schema(); + + my %phenostore_stocks = (); + my $treatments = $self->get_treatments(); + my $phenostore_data_hash = {}; + my $phenosearch = CXGN::Phenotypes::SearchFactory->instantiate( + 'Native', { + bcs_schema => $chado_schema, + trait_list => [map {$_->{trait_id}} @{$treatments}], + trial_list => [$self->get_trial_id()], + data_level => 'plant' + }); + my $treatment_data = $phenosearch->search(); + my $plant_pheno = {}; + foreach my $row (@{$treatment_data}) { + $plant_pheno->{$row->{obsunit_uniquename}}->{$row->{trait_name}} = $row->{phenotype_value}; + } my $create_tissue_sample_entries_txn = sub { - my $chado_schema = $self->bcs_schema(); my $layout = CXGN::Trial::TrialLayout->new( { schema => $chado_schema, trial_id => $self->get_trial_id(), experiment_type=>'field_layout' }); my $design = $layout->get_design(); @@ -3966,7 +4218,6 @@ sub create_tissue_samples { my $tissue_type_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'tissue_type', 'stock_property')->cvterm_id(); my $replicate_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'replicate', 'stock_property')->cvterm_id(); my $field_layout_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'field_layout', 'experiment_type')->cvterm_id(); - my $treatment_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id(); my $rs_previous_tissue = $chado_schema->resultset("Project::Projectprop")->search({ type_id => $has_tissues_cvterm, @@ -3978,39 +4229,6 @@ sub create_tissue_samples { } my $new_tissue_number = $previous_tissue_number + ($use_tissue_numbers ? scalar(@$tissue_names) : 0); - my $treatments; - my %treatment_experiments; - my %treatment_plots; - my %treatment_subplots; - if ($inherits_plot_treatments){ - $treatments = $self->get_treatments(); - foreach (@$treatments){ - - my $rs = $chado_schema->resultset('Project::Projectprop')->update_or_create({ - type_id => $has_tissues_cvterm, - project_id => $_->[0], - rank => 0, - value => $new_tissue_number - }, - { - key=>'projectprop_c1' - }); - - my $treatment_nd_experiment = $chado_schema->resultset("Project::Project")->search( { 'me.project_id' => $_->[0] }, {select=>['nd_experiment.nd_experiment_id']})->search_related('nd_experiment_projects')->search_related('nd_experiment', { 'nd_experiment.type_id' => $treatment_cvterm })->single(); - $treatment_experiments{$_->[0]} = $treatment_nd_experiment->nd_experiment_id(); - - my $treatment_trial = CXGN::Trial->new({ bcs_schema => $chado_schema, trial_id => $_->[0]}); - my $plots = $treatment_trial->get_plots(); - foreach my $plot (@$plots){ - $treatment_plots{$_->[0]}->{$plot->[0]} = 1; - } - my $subplots = $treatment_trial->get_subplots(); - foreach my $subplot (@$subplots){ - $treatment_subplots{$_->[0]}->{$subplot->[0]} = 1; - } - } - } - my $rs = $chado_schema->resultset('Project::Projectprop')->update_or_create({ type_id => $has_tissues_cvterm, project_id => $self->get_trial_id(), @@ -4035,7 +4253,7 @@ sub create_tissue_samples { if (! $plant_row) { print STDERR "The plant $plant_name is not found in the database\n"; - return "The plant $plant_name is not yet in the database. Cannot create tissue entries."; + die "The plant $plant_name is not yet in the database. Cannot create tissue entries."; } my $parent_plant = $plant_row->stock_id(); @@ -4052,6 +4270,20 @@ sub create_tissue_samples { } print STDERR "... ... creating tissue $tissue_sample_name...\n"; + foreach my $treatment (@{$treatments}) { + my $treatment_name = $treatment->{trait_name}; + if (exists($plant_pheno->{$plant_name}->{$treatment_name})) { + $phenostore_data_hash->{$tissue_sample_name}->{$treatment_name} = [ + $plant_pheno->{$plant_name}->{$treatment_name}, + $phenotype_store_config->{metadata_hash}->{date}, + $phenotype_store_config->{metadata_hash}->{operator}, + '', + '' + ]; + $phenostore_stocks{$tissue_sample_name} = 1; + } + } + my $plant_accession_rs = $self->bcs_schema()->resultset("Stock::StockRelationship")->search({'me.subject_id'=>$parent_plant, 'me.type_id'=>$plant_relationship_cvterm, 'object.type_id'=>[$accession_cvterm, $cross_cvterm, $family_name_cvterm]}, {'join'=>'object'}); if ($plant_accession_rs->count != 1){ die "There is not 1 stock_relationship of type plant_of between the plant $parent_plant and an accession, a cross or a family_name."; @@ -4068,17 +4300,6 @@ sub create_tissue_samples { push @tissue_subjects, { type_id => $tissue_relationship_cvterm, object_id => $plant_accession_rs->first->object_id }; push @tissue_nd_experiment_stocks, { nd_experiment_id => $field_layout_experiment->nd_experiment_id(), type_id => $field_layout_cvterm }; - if ($inherits_plot_treatments){ - if($treatments){ - foreach (@$treatments){ - my $plots = $treatment_plots{$_->[0]}; - if (exists($plots->{$parent_plot_id})){ - push @tissue_nd_experiment_stocks, { nd_experiment_id => $treatment_experiments{$_->[0]}, type_id => $treatment_cvterm }; - } - } - } - } - my $tissue = $chado_schema->resultset("Stock::Stock")->create({ organism_id => $parent_plant_organism, name => $tissue_sample_name, @@ -4112,35 +4333,53 @@ sub create_tissue_samples { subject_id => $t, type_id => $tissue_relationship_cvterm, }); - - if ($inherits_plot_treatments){ - if($treatments){ - foreach (@$treatments){ - my $subplots = $treatment_subplots{$_->[0]}; - if (exists($subplots->{$subplot_row->stock_id})){ - my $plant_nd_experiment_stock = $chado_schema->resultset("NaturalDiversity::NdExperimentStock")->create({ - nd_experiment_id => $treatment_experiments{$_->[0]}, - type_id => $treatment_cvterm, - stock_id => $t, - }); - } - } - } - } } } } } - foreach (@$treatments) { - my $layout = CXGN::Trial::TrialLayout->new( { schema => $chado_schema, trial_id => $_->[0], experiment_type=>'field_layout' }); - $layout->generate_and_cache_layout(); - } $layout->generate_and_cache_layout(); }; eval { $self->bcs_schema()->txn_do($create_tissue_sample_entries_txn); + + if ($inherits_plot_treatments && $treatments) { + + my @treatment_names = map {$_->{trait_name}} @{$treatments}; + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $phenotype_store_config->{basepath}, + dbhost => $phenotype_store_config->{dbhost}, + dbname => $phenotype_store_config->{dbname}, + dbuser => $phenotype_store_config->{dbuser}, + dbpass => $phenotype_store_config->{dbpass}, + temp_file_nd_experiment_id => $phenotype_store_config->{temp_file_nd_experiment_id}, + bcs_schema => $chado_schema, + metadata_schema => $self->metadata_schema, + phenome_schema => $self->phenome_schema, + user_id => $phenotype_store_config->{user_id}, + stock_list => [keys(%phenostore_stocks)], + trait_list => \@treatment_names, + values_hash => $phenostore_data_hash, + metadata_hash => $phenotype_store_config->{metadata_hash} + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + die $verified_error; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + die "An error occurred inheriting treatments: $stored_phenotype_error\n"; + } + } }; if ($@) { print STDERR "An error occurred creating the tissue sample entities. $@\n"; @@ -4242,9 +4481,27 @@ sub create_subplot_entities { my $inherits_plot_treatments = shift; my $subplot_owner = shift; my $subplot_owner_username = shift; + my $phenotype_store_config = shift; + + my $chado_schema = $self->bcs_schema(); + + my %phenostore_stocks = (); + my $treatments = $self->get_treatments(); + my $phenostore_data_hash = {}; + my $phenosearch = CXGN::Phenotypes::SearchFactory->instantiate( + 'Native', { + bcs_schema => $chado_schema, + trait_list => [map {$_->{trait_id}} @{$treatments}], + trial_list => [$self->get_trial_id()], + data_level => 'plot' + }); + my $treatment_data = $phenosearch->search(); + my $plot_pheno = {}; + foreach my $row (@{$treatment_data}) { + $plot_pheno->{$row->{obsunit_uniquename}}->{$row->{trait_name}} = $row->{phenotype_value}; + } my $create_subplot_entities_txn = sub { - my $chado_schema = $self->bcs_schema(); my $layout = CXGN::Trial::TrialLayout->new( { schema => $chado_schema, trial_id => $self->get_trial_id(), experiment_type=>'field_layout' }); my $design = $layout->get_design(); @@ -4261,33 +4518,8 @@ sub create_subplot_entities { my $replicate_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'replicate', 'stock_property')->cvterm_id(); my $has_subplots_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_subplot_entries', 'project_property')->cvterm_id(); my $field_layout_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'field_layout', 'experiment_type')->cvterm_id(); - my $treatment_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id(); #my $subplots_per_plot_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'subplots_per_plot', 'project_property')->cvterm_id(); - my $treatments; - my %treatment_experiments; - my %treatment_plots; - if ($inherits_plot_treatments){ - $treatments = $self->get_treatments(); - foreach (@$treatments){ - - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ - type_id => $has_subplots_cvterm, - value => $subplots_per_plot, - project_id => $_->[0], - }); - - my $treatment_nd_experiment = $chado_schema->resultset("Project::Project")->search( { 'me.project_id' => $_->[0] }, {select=>['nd_experiment.nd_experiment_id']})->search_related('nd_experiment_projects')->search_related('nd_experiment', { 'nd_experiment.type_id' => $treatment_cvterm })->single(); - $treatment_experiments{$_->[0]} = $treatment_nd_experiment->nd_experiment_id(); - - my $treatment_trial = CXGN::Project->new({ bcs_schema => $chado_schema, trial_id => $_->[0]}); - my $plots = $treatment_trial->get_plots(); - foreach my $plot (@$plots){ - $treatment_plots{$_->[0]}->{$plot->[0]} = 1; - } - } - } - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ type_id => $has_subplots_cvterm, value => $subplots_per_plot, @@ -4302,7 +4534,7 @@ sub create_subplot_entities { if (! $plot_row) { print STDERR "The plot $plot is not found in the database\n"; - return "The plot $plot is not yet in the database. Cannot create subplot entries."; + die "The plot $plot is not yet in the database. Cannot create subplot entries."; } my $parent_plot = $plot_row->stock_id(); @@ -4311,12 +4543,26 @@ sub create_subplot_entities { foreach my $subplot_index_number (1..$subplots_per_plot) { my $subplot_name = $parent_plot_name."_subplot_$subplot_index_number"; + + foreach my $treatment (@{$treatments}) { + my $treatment_name = $treatment->{trait_name}; + if (exists($plot_pheno->{$parent_plot_name}->{$treatment_name})) { + $phenostore_data_hash->{$subplot_name}->{$treatment_name} = [ + $plot_pheno->{$parent_plot_name}->{$treatment_name}, + $phenotype_store_config->{metadata_hash}->{date}, + $phenotype_store_config->{metadata_hash}->{operator}, + '', + '' + ]; + $phenostore_stocks{$subplot_name} = 1; + } + } #print STDERR "... ... creating subplot $subplot_name...\n"; $self->_save_subplot_entry($chado_schema, $accession_cvterm, $cross_cvterm, $family_name_cvterm, $parent_plot_organism, $parent_plot_name, $parent_plot, $subplot_name, $subplot_cvterm, $subplot_index_number, $subplot_index_number_cvterm, $block_cvterm, $plot_number_cvterm, - $replicate_cvterm, $subplot_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, $inherits_plot_treatments, $treatments, - $plot_relationship_cvterm, \%treatment_plots, \%treatment_experiments, $treatment_cvterm, $subplot_owner, $subplot_owner_username); + $replicate_cvterm, $subplot_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, + $plot_relationship_cvterm, $subplot_owner, $subplot_owner_username); } } @@ -4325,6 +4571,43 @@ sub create_subplot_entities { eval { $self->bcs_schema()->txn_do($create_subplot_entities_txn); + + if ($inherits_plot_treatments && $treatments) { + + my @treatment_names = map {$_->{trait_name}} @{$treatments}; + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $phenotype_store_config->{basepath}, + dbhost => $phenotype_store_config->{dbhost}, + dbname => $phenotype_store_config->{dbname}, + dbuser => $phenotype_store_config->{dbuser}, + dbpass => $phenotype_store_config->{dbpass}, + temp_file_nd_experiment_id => $phenotype_store_config->{temp_file_nd_experiment_id}, + bcs_schema => $chado_schema, + metadata_schema => $self->metadata_schema, + phenome_schema => $self->phenome_schema, + user_id => $phenotype_store_config->{user_id}, + stock_list => [keys(%phenostore_stocks)], + trait_list => \@treatment_names, + values_hash => $phenostore_data_hash, + metadata_hash => $phenotype_store_config->{metadata_hash} + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + die $verified_error; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + die "An error occurred inheriting treatments: $stored_phenotype_error\n"; + } + } }; if ($@) { print STDERR "An error occurred creating the subplot entities. $@\n"; @@ -4355,9 +4638,29 @@ sub save_subplot_entries { my $parsed_data = shift; my $subplots_per_plot = shift; my $inherits_plot_treatments = shift; + my $owner_id = shift; + my $owner_name = shift; + my $phenotype_store_config = shift; + + my $chado_schema = $self->bcs_schema(); + + my %phenostore_stocks = (); + my $treatments = $self->get_treatments(); + my $phenostore_data_hash = {}; + my $phenosearch = CXGN::Phenotypes::SearchFactory->instantiate( + 'Native', { + bcs_schema => $chado_schema, + trait_list => [map {$_->{trait_id}} @{$treatments}], + trial_list => [$self->get_trial_id()], + data_level => 'plot' + }); + my $treatment_data = $phenosearch->search(); + my $plot_pheno = {}; + foreach my $row (@{$treatment_data}) { + $plot_pheno->{$row->{obsunit_uniquename}}->{$row->{trait_name}} = $row->{phenotype_value}; + } my $create_subplot_entities_txn = sub { - my $chado_schema = $self->bcs_schema(); my $layout = CXGN::Trial::TrialLayout->new( { schema => $chado_schema, trial_id => $self->get_trial_id(), experiment_type=>'field_layout' }); my $design = $layout->get_design(); @@ -4374,33 +4677,8 @@ sub save_subplot_entries { my $replicate_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'replicate', 'stock_property')->cvterm_id(); my $has_subplots_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_subplot_entries', 'project_property')->cvterm_id(); my $field_layout_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'field_layout', 'experiment_type')->cvterm_id(); - my $treatment_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id(); #my $subplots_per_plot_cvterm = SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'subplots_per_plot', 'project_property')->cvterm_id(); - my $treatments; - my %treatment_experiments; - my %treatment_plots; - if ($inherits_plot_treatments){ - $treatments = $self->get_treatments(); - foreach (@$treatments){ - - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ - type_id => $has_subplots_cvterm, - value => $subplots_per_plot, - project_id => $_->[0], - }); - - my $treatment_nd_experiment = $chado_schema->resultset("Project::Project")->search( { 'me.project_id' => $_->[0] }, {select=>['nd_experiment.nd_experiment_id']})->search_related('nd_experiment_projects')->search_related('nd_experiment', { 'nd_experiment.type_id' => $treatment_cvterm })->single(); - $treatment_experiments{$_->[0]} = $treatment_nd_experiment->nd_experiment_id(); - - my $treatment_trial = CXGN::Trial->new({ bcs_schema => $chado_schema, trial_id => $_->[0]}); - my $plots = $treatment_trial->get_plots(); - foreach my $plot (@$plots){ - $treatment_plots{$_->[0]}->{$plot->[0]} = 1; - } - } - } - my $rs = $chado_schema->resultset("Project::Projectprop")->find_or_create({ type_id => $has_subplots_cvterm, value => $subplots_per_plot, @@ -4418,7 +4696,7 @@ sub save_subplot_entries { if (!$plot_row) { print STDERR "The plot $plot_name is not found in the database\n"; - return "The plot $plot_name is not yet in the database. Cannot create subplot entries."; + die "The plot $plot_name is not yet in the database. Cannot create subplot entries."; } my $parent_plot = $plot_row->stock_id(); @@ -4430,10 +4708,25 @@ sub save_subplot_entries { my $subplot_index_numbers = $val->{subplot_index_numbers}; my $increment = 0; foreach my $subplot_name (@$subplot_names) { + + foreach my $treatment (@{$treatments}) { + my $treatment_name = $treatment->{trait_name}; + if (exists($plot_pheno->{$parent_plot_name}->{$treatment_name})) { + $phenostore_data_hash->{$subplot_name}->{$treatment_name} = [ + $plot_pheno->{$parent_plot_name}->{$treatment_name}, + $phenotype_store_config->{metadata_hash}->{date}, + $phenotype_store_config->{metadata_hash}->{operator}, + '', + '' + ]; + $phenostore_stocks{$subplot_name} = 1; + } + } + my $given_subplot_index_number = $subplot_index_numbers->[$increment]; my $subplot_index_number_save = $given_subplot_index_number ? $given_subplot_index_number : $subplot_index_number; - $self->_save_subplot_entry($chado_schema, $accession_cvterm, $cross_cvterm, $family_name_cvterm, $parent_plot_organism, $parent_plot_name, $parent_plot, $subplot_name, $subplot_cvterm, $subplot_index_number_save, $subplot_index_number_cvterm, $block_cvterm, $plot_number_cvterm, $replicate_cvterm, $subplot_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, $inherits_plot_treatments, $treatments, $plot_relationship_cvterm, \%treatment_plots, \%treatment_experiments, $treatment_cvterm); + $self->_save_subplot_entry($chado_schema, $accession_cvterm, $cross_cvterm, $family_name_cvterm, $parent_plot_organism, $parent_plot_name, $parent_plot, $subplot_name, $subplot_cvterm, $subplot_index_number_save, $subplot_index_number_cvterm, $block_cvterm, $plot_number_cvterm, $replicate_cvterm, $subplot_relationship_cvterm, $field_layout_experiment, $field_layout_cvterm, $plot_relationship_cvterm); $subplot_index_number++; $increment++; } @@ -4444,6 +4737,42 @@ sub save_subplot_entries { eval { $self->bcs_schema()->txn_do($create_subplot_entities_txn); + + if ($inherits_plot_treatments && $treatments) { + my @treatment_names = map {$_->{trait_name}} @{$treatments}; + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $phenotype_store_config->{basepath}, + dbhost => $phenotype_store_config->{dbhost}, + dbname => $phenotype_store_config->{dbname}, + dbuser => $phenotype_store_config->{dbuser}, + dbpass => $phenotype_store_config->{dbpass}, + temp_file_nd_experiment_id => $phenotype_store_config->{temp_file_nd_experiment_id}, + bcs_schema => $chado_schema, + metadata_schema => $self->metadata_schema, + phenome_schema => $self->phenome_schema, + user_id => $phenotype_store_config->{user_id}, + stock_list => [keys(%phenostore_stocks)], + trait_list => \@treatment_names, + values_hash => $phenostore_data_hash, + metadata_hash => $phenotype_store_config->{metadata_hash} + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + die $verified_error; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + die "An error occurred inheriting treatments: $stored_phenotype_error\n"; + } + } }; if ($@) { print STDERR "An error occurred creating the subplot entities. $@\n"; @@ -4473,16 +4802,9 @@ sub _save_subplot_entry { my $subplot_relationship_cvterm = shift; my $field_layout_experiment = shift; my $field_layout_cvterm = shift; - my $inherits_plot_treatments = shift; - my $treatments = shift; my $plot_relationship_cvterm = shift; - my $treatment_plots_ref = shift; - my $treatment_experiments_ref = shift; - my $treatment_cvterm = shift; my $subplot_owner = shift; my $subplot_owner_username = shift; - my %treatment_plots = %$treatment_plots_ref; - my %treatment_experiments = %$treatment_experiments_ref; my $subplot = $chado_schema->resultset("Stock::Stock")->create({ organism_id => $parent_plot_organism, @@ -4537,21 +4859,6 @@ sub _save_subplot_entry { type_id => $field_layout_cvterm, stock_id => $subplot->stock_id(), }); - - if ($inherits_plot_treatments){ - if($treatments){ - foreach (@$treatments){ - my $plots = $treatment_plots{$_->[0]}; - if (exists($plots->{$parent_plot})){ - my $subplot_nd_experiment_stock = $chado_schema->resultset("NaturalDiversity::NdExperimentStock")->create({ - nd_experiment_id => $treatment_experiments{$_->[0]}, - type_id => $treatment_cvterm, - stock_id => $subplot->stock_id(), - }); - } - } - } - } } =head2 function has_subplot_entries() @@ -5106,9 +5413,9 @@ sub get_controls_by_plot { =head2 get_treatments - Usage: $plants = $t->get_treatments(); + Usage: $treatment_summary = $t->get_treatments(); Desc: retrieves the treatments that are part of this trial - Ret: an array ref containing from project table [ treatment_name, treatment_id ] + Ret: an array ref of hash refs containing { trait_id, trait_name, count } Args: Side Effects: Example: @@ -5117,6 +5424,38 @@ sub get_controls_by_plot { sub get_treatments { my $self = shift; + + my $all_traits = $self->get_traits_assayed(); #[$trait_id, $trait_name, $component_terms, $count, $imaging_project_id, $imaging_project_name]; + + my @treatment_traits = grep {$_->[1] =~ /_TREATMENT:/} @{$all_traits}; + + my @return_data; + + foreach my $treatment (@treatment_traits) { + push @return_data, { + trait_id => $treatment->[0], + trait_name => $treatment->[1], + count => $treatment->[3] + }; + } + + return \@return_data; +} + +=head2 get_treatment_projects (DEPRECATED) + + Usage: DEPRECATED $treatment_summary = $t->get_treatment_projects(); + Desc: DEPRECATED retrieves the treatments that are part of this trial + Ret: DEPRECATED an array ref containing from project table [ treatment_id, treatment_name] + Args: + Side Effects: + Example: + +=cut + +sub get_treatment_projects { #WARNING: THIS FUNCTION IS DEPRECATD AND SHOULD NOT BE USED + my $self = shift; + my @plants; my $treatment_rel_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, "trial_treatment_relationship", "project_relationship")->cvterm_id(); @@ -5127,6 +5466,7 @@ sub get_treatments { push @treatments, [$rs->project_id, $rs->name]; } return \@treatments; + } =head2 get_trial_contacts @@ -5254,7 +5594,7 @@ sub suppress_plot_phenotype { =head2 delete_assayed_trait -Usage: my $delete_trait_return_error = $trial->delete_assayed_trait($c->config->{basepath}, $c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, $phenotypes_ids, [] ); +Usage: my $delete_trait_return_error = $trial->delete_assayed_trait($c->config->{basepath}, $c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass},$tempfile_id, $phenotypes_ids, [] ); if ($delete_trait_return_error) { $c->stash->{rest} = { error => $delete_trait_return_error }; return; diff --git a/lib/CXGN/Stock.pm b/lib/CXGN/Stock.pm index 3df4ae74e6..60e34b7cac 100644 --- a/lib/CXGN/Stock.pm +++ b/lib/CXGN/Stock.pm @@ -41,6 +41,7 @@ use Bio::GeneticRelationships::Individual; use base qw / CXGN::DB::Object / ; use CXGN::Stock::StockLookup; use Try::Tiny; +use Scalar::Util qw(refaddr); use CXGN::Metadata::Metadbdata; use File::Basename qw | basename dirname|; @@ -1947,7 +1948,343 @@ sub add_synonym { $stock->create_stockprops({$synonym_cvterm->name() => $synonym}); } +=head2 get_child_stocks +Usage: $self->get_child_stocks +Desc: retrieves a structured hash of all child stocks (subplots, plants, tissue samples). Self is included at the top. +Ret: a scalar hash +Args: +Side effects: +Example: + +=cut + +sub get_child_stocks { + my $self = shift; + my $type = $self->type(); + my $stock_id = $self->stock_id(); + my $name = $self->uniquename(); + + if (!$type) { + die "Cannot get child stocks without knowing stock type!\n"; + } + + my $plot_and_plant_q = "SELECT stock.stock_id, stock.name, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock.type_id) AS stock_type, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock_relationship.type_id) AS relationship_type + FROM stock_relationship + JOIN stock ON (stock.stock_id=stock_relationship.object_id) + WHERE stock_relationship.subject_id=?"; #For plots, this returns accessions, subplots, and plants. For plants, this returns parent subplot and accessions. Also gives accessions for subplots. + + my $subplot_q = "SELECT stock.stock_id, stock.name, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock.type_id) AS stock_type, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock_relationship.type_id) AS relationship_type + FROM stock_relationship + JOIN stock ON (stock.stock_id=stock_relationship.subject_id) + WHERE stock_relationship.object_id=?;"; #gets parent plot and child plants + + my $stockprops_q = "SELECT cvterm.name, cvterm_id, value FROM stockprop + JOIN cvterm ON (cvterm.cvterm_id=stockprop.type_id) + WHERE stockprop.stock_id=?"; #gets all stockprops for any stock. + + my $tissue_sample_q = "SELECT * FROM + (SELECT stock.stock_id, stock.name, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock.type_id) AS stock_type, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock_relationship.type_id) AS relationship_type + FROM stock_relationship + JOIN stock ON (stock.stock_id=stock_relationship.subject_id) + WHERE stock_relationship.object_id=?) AS tissue_samples_subquery + WHERE stock_type='tissue_sample'"; #only useful for plants, where it gives tissue samples. + + my $stock_structure = { + stock_id => $stock_id, + type => $type, + name => $name, + attributes => {}, + has => {} + }; + + my $h = $self->schema()->storage()->dbh()->prepare($stockprops_q); + $h->execute($stock_id); + + while (my ($stockprop, $stockprop_id, $value) = $h->fetchrow_array()) {# get this stock's stockprops + $stock_structure->{attributes}->{$stockprop} = { + id => $stockprop_id, + value => $value + }; + } + + if ($type eq "plot") { + $h = $self->schema()->storage()->dbh()->prepare($plot_and_plant_q); + $h->execute($stock_id); + + my @child_stocks = (); + + while (my ($child_stock_id, $child_stock_name, $child_type, $relationship_type) = $h->fetchrow_array()){ + push @child_stocks, { + stock_id => $child_stock_id, + stock_name => $child_stock_name, + type => $child_type, + relationship_type => $relationship_type + }; + } + + my @has_subplots = grep {$_->{type} eq "subplot"} @child_stocks; + my @has_plants = grep {$_->{type} eq "plant"} @child_stocks; + my @accessions = grep {$_->{type} eq "accession"} @child_stocks; + + if (@has_subplots) { #if there are subplots, we can safely assume that plants are children of the subplots + foreach my $subplot (@has_subplots) { + my $child_stock = CXGN::Stock->new({ + schema => $self->schema(), + stock_id => $subplot->{stock_id}, + type => $subplot->{type} + }); + $stock_structure->{has}->{"".$subplot->{stock_name}.""} = $child_stock->get_child_stocks(); + } + } elsif (@has_plants) { + foreach my $plant (@has_plants) { + my $child_stock = CXGN::Stock->new({ + schema => $self->schema(), + stock_id => $plant->{stock_id}, + type => $plant->{type} + }); + $stock_structure->{has}->{"".$plant->{stock_name}.""} = $child_stock->get_child_stocks(); + } + } else { # no plants or subplots, just accessions + foreach my $accession (@accessions) { + my $child_stock = CXGN::Stock->new({ + schema => $self->schema(), + stock_id => $accession->{stock_id}, + type => $accession->{type} + }); + $stock_structure->{has}->{''.$accession->{stock_name}.""} = $child_stock->get_child_stocks(); + } + } + + } elsif ($type eq "subplot") { + + my @child_stocks; + + $h = $self->schema()->storage()->dbh()->prepare($plot_and_plant_q); + $h->execute($stock_id); + + while (my ($child_stock_id, $child_stock_name, $child_type, $relationship_type) = $h->fetchrow_array()){ + push @child_stocks, { + stock_id => $child_stock_id, + stock_name => $child_stock_name, + type => $child_type, + relationship_type => $relationship_type + }; + } + + $h = $self->schema()->storage()->dbh()->prepare($subplot_q); # this will also grab parent plot, which will be filtered out + $h->execute($stock_id); + + while (my ($child_stock_id, $child_stock_name, $child_type, $relationship_type) = $h->fetchrow_array()){ + push @child_stocks, { + stock_id => $child_stock_id, + stock_name => $child_stock_name, + type => $child_type, + relationship_type => $relationship_type + }; + } + + @child_stocks = grep {$_->{type} ne "plot"} @child_stocks; #remove parent plot + + my @has_plants = grep {$_->{type} eq "plant"} @child_stocks; + + if (@has_plants) { + foreach my $plant (@has_plants) { + my $child_stock = CXGN::Stock->new({ + schema => $self->schema(), + stock_id => $plant->{stock_id}, + type => $plant->{type} + }); + $stock_structure->{has}->{"".$plant->{stock_name}.""} = $child_stock->get_child_stocks(); + } + } else {# if no plants, subplots can only have accessions + foreach my $accession (@child_stocks) { + my $child_stock = CXGN::Stock->new({ + schema => $self->schema(), + stock_id => $accession->{stock_id}, + type => $accession->{type} + }); + $stock_structure->{has}->{"".$accession->{stock_name}.""} = $child_stock->get_child_stocks(); + } + } + + } elsif ($type eq "plant") { + + my @child_stocks; + + $h = $self->schema()->storage()->dbh()->prepare($plot_and_plant_q);# this will also grab parent subplot, which will be filtered out + $h->execute($stock_id); + + while (my ($child_stock_id, $child_stock_name, $child_type, $relationship_type) = $h->fetchrow_array()){ + push @child_stocks, { + stock_id => $child_stock_id, + stock_name => $child_stock_name, + type => $child_type, + relationship_type => $relationship_type + }; + } + + $h = $self->schema()->storage()->dbh()->prepare($tissue_sample_q); + $h->execute($stock_id); + + while (my ($child_stock_id, $child_stock_name, $child_type, $relationship_type) = $h->fetchrow_array()){ + push @child_stocks, { + stock_id => $child_stock_id, + stock_name => $child_stock_name, + type => $child_type, + relationship_type => $relationship_type + }; + } + + @child_stocks = grep {$_->{type} ne "subplot"} @child_stocks; #remove parent subplot (if any) + + # at this point, all child stocks are either tissue samples or accessions, which means we no longer care what is in them + foreach my $child (@child_stocks) { + my $child_stock = CXGN::Stock->new({ + schema => $self->schema(), + stock_id => $child->{stock_id}, + type => $child->{type} + }); + $stock_structure->{has}->{"".$child->{stock_name}.""} = $child_stock->get_child_stocks(); + } + + } elsif ($type eq "tissue_sample") { + delete $stock_structure->{has}; + return $stock_structure; + } elsif ($type eq "accession") { + delete $stock_structure->{has}; + return $stock_structure; + } + + return $stock_structure; +} + +=head2 get_child_stocks_flat_list + +Same as get_child_stocks, but returns just a flat listref and not a hierarchy. Stockprops are ommitted. +Listref elements are hashrefs with structure {stock_id, name, type}. This stock is dropped in the list, +unlike in get_child_stocks where it remains at the top of the hierarchy. + +=cut + +sub get_child_stocks_flat_list { + my $self = shift; + my $type = $self->type(); + my $stock_id = $self->stock_id(); + my $name = $self->uniquename(); + + if (!$type) { + die "Cannot get child stocks without knowing stock type!\n"; + } + + my $plot_and_plant_q = "SELECT stock.stock_id, stock.name, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock.type_id) AS stock_type, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock_relationship.type_id) AS relationship_type + FROM stock_relationship + JOIN stock ON (stock.stock_id=stock_relationship.object_id) + WHERE stock_relationship.subject_id=?"; #For plots, this returns accessions, subplots, and plants. For plants, this returns parent subplot and accessions. Also gives accessions for subplots. + + my $subplot_q = "SELECT stock.stock_id, stock.name, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock.type_id) AS stock_type, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock_relationship.type_id) AS relationship_type + FROM stock_relationship + JOIN stock ON (stock.stock_id=stock_relationship.subject_id) + WHERE stock_relationship.object_id=?;"; #gets parent plot and child plants + + my $tissue_sample_q = "SELECT * FROM + (SELECT stock.stock_id, stock.name, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock.type_id) AS stock_type, + (SELECT cvterm.name FROM cvterm WHERE cvterm_id=stock_relationship.type_id) AS relationship_type + FROM stock_relationship + JOIN stock ON (stock.stock_id=stock_relationship.subject_id) + WHERE stock_relationship.object_id=?) AS tissue_samples_subquery + WHERE stock_type='tissue_sample'"; #only useful for plants, where it gives tissue samples. + + my $stock_structure = []; + + my $h; + + if ($type eq "plot") { + $h = $self->schema()->storage()->dbh()->prepare($plot_and_plant_q); + $h->execute($stock_id); + + my @child_stocks = (); + + while (my ($child_stock_id, $child_stock_name, $child_type, $relationship_type) = $h->fetchrow_array()){ + next if ($child_type eq 'accession'); + push @{$stock_structure}, { + stock_id => $child_stock_id, + name => $child_stock_name, + type => $child_type + }; + } + + my @has_plants = grep {$_->{type} eq "plant"} @{$stock_structure}; + + if (@has_plants) { #need to get possible tissue samples if there are plants + foreach my $plant (@has_plants) { + my $child_stock = CXGN::Stock->new({ + schema => $self->schema(), + stock_id => $plant->{stock_id}, + type => $plant->{type} + }); + push @{$stock_structure}, @{$child_stock->get_child_stocks_flat_list()}; + } + } + + } elsif ($type eq "subplot") { + + $h = $self->schema()->storage()->dbh()->prepare($subplot_q); # this will also grab parent plot, which will be filtered out + $h->execute($stock_id); + + while (my ($child_stock_id, $child_stock_name, $child_type, $relationship_type) = $h->fetchrow_array()){ + next if ($child_type eq "plot"); + push @{$stock_structure}, { + stock_id => $child_stock_id, + name => $child_stock_name, + type => $child_type + }; + } + + my @has_plants = grep {$_->{type} eq "plant"} @{$stock_structure}; + + if (@has_plants) { + foreach my $plant (@has_plants) { + my $child_stock = CXGN::Stock->new({ + schema => $self->schema(), + stock_id => $plant->{stock_id}, + type => $plant->{type} + }); + push @{$stock_structure}, @{$child_stock->get_child_stocks_flat_list()}; + } + } + } elsif ($type eq "plant") { + + $h = $self->schema()->storage()->dbh()->prepare($tissue_sample_q); + $h->execute($stock_id); + + while (my ($child_stock_id, $child_stock_name, $child_type, $relationship_type) = $h->fetchrow_array()){ + push @{$stock_structure}, { + stock_id => $child_stock_id, + stock_name => $child_stock_name, + type => $child_type + }; + } + + } elsif ($type eq "tissue_sample") { + return []; + } elsif ($type eq "accession") { + return []; + } + + return $stock_structure; +} =head2 merge() diff --git a/lib/CXGN/Stock/Plot.pm b/lib/CXGN/Stock/Plot.pm index 212444f183..fa26fe7989 100644 --- a/lib/CXGN/Stock/Plot.pm +++ b/lib/CXGN/Stock/Plot.pm @@ -124,7 +124,7 @@ $plot_data = { } } -The data structure follows plot->(subplot->)(plant->)accession. If this stock is not a plot, undef is returned. +The data structure follows plot->(subplot->)(plant->(tissue_sample))accession. If this stock is not a plot, undef is returned. =cut diff --git a/lib/CXGN/Trait.pm b/lib/CXGN/Trait.pm index 9925de612d..e442da7b97 100644 --- a/lib/CXGN/Trait.pm +++ b/lib/CXGN/Trait.pm @@ -4,12 +4,15 @@ package CXGN::Trait; use Moose; use Data::Dumper; use Try::Tiny; +use List::Util qw(max); use JSON; +use CXGN::Onto; use CXGN::BrAPI::v2::ExternalReferences; use CXGN::BrAPI::v2::Methods; use CXGN::BrAPI::v2::Scales; use CXGN::BrAPI::Exceptions::ConflictException; use CXGN::BrAPI::Exceptions::ServerException; +use CXGN::List::Transform; ## to do: add concept of trait short name; provide alternate constructors for term, shortname, and synonyms etc. @@ -202,8 +205,8 @@ has 'format' => (isa => 'Str', ); has 'default_value' => ( - isa => 'Str', - is => 'ro', + isa => 'Maybe[Str]', + is => 'rw', lazy => 1, default => sub { my $self = shift; @@ -215,13 +218,13 @@ has 'default_value' => ( if ($row) { return $row->value(); } - return ""; + return; } ); has 'minimum' => ( - isa => 'Str', - is => 'ro', + isa => 'Maybe[Str]', + is => 'rw', lazy => 1, default => sub { my $self = shift; @@ -233,13 +236,13 @@ has 'minimum' => ( if ($row) { return $row->value(); } - return ""; + return; } ); has 'maximum' => ( - isa => 'Str', - is => 'ro', + isa => 'Maybe[Str]', + is => 'rw', lazy => 1, default => sub { my $self = shift; @@ -251,13 +254,13 @@ has 'maximum' => ( if ($row) { return $row->value(); } - return ""; + return; } ); has 'categories' => ( - isa => 'Str', - is => 'ro', + isa => 'Maybe[Str]', + is => 'rw', lazy => 1, default => sub { my $self = shift; @@ -269,7 +272,43 @@ has 'categories' => ( if ($row) { return $row->value(); } - return ""; + return; + } +); + +has 'category_details' => ( + isa => 'Maybe[Str]', + is => 'rw', + lazy => 1, + default => sub { + my $self = shift; + my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find( + { cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_details' }, + { join => 'type'} + ); + + if ($row) { + return $row->value(); + } + return; + } +); + +has 'repeat_type' => ( + isa => 'Maybe[Str]', + is => 'rw', + lazy => 1, + default => sub { + my $self = shift; + my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find( + { cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_repeat_type' }, + { join => 'type'} + ); + + if ($row) { + return $row->value(); + } + return; } ); @@ -454,7 +493,7 @@ sub store { my $context = SGN::Context->new; my $cv_name = $context->get_conf('trait_ontology_cv_name'); my $cvterm_name = $context->get_conf('trait_ontology_cvterm_name'); - my $ontology_name = $context->get_conf('trait_ontology_db_name'); + my $ontology_name = $context->get_conf('trait_ontology_db_name'); #this is the only config key that actually gets used elsewhere # Get trait attributes cvterm ids my $trait_entity_id = $self->trait_entity_id; @@ -754,6 +793,269 @@ sub delete_existing_synonyms { )->delete; } +sub interactive_store { + my $self = shift; + my $parent_term = shift; + + my $schema = $self->bcs_schema(); + + my $parent_id; + + my $name = $self->name() || die "No name found.\n"; + my $definition = $self->definition() || die "No definition found.\n"; + my $format = $self->format() || die "No format found.\n"; + my $default_value = $self->default_value() ne "" ? $self->default_value() : undef; + my $minimum = $self->minimum() ne "" ? $self->minimum() : undef; + my $maximum = $self->maximum() ne "" ? $self->maximum() : undef; + my $categories = $self->categories() ne "" ? $self->categories() : undef; + my $repeat_type = $self->repeat_type() ne "" ? $self->repeat_type () : undef; + my $category_details = $self->category_details() ne "" ? $self->category_details() : undef; + + my $trait_property_cv_id = $schema->resultset("Cv::Cv")->find({name => 'trait_property'})->cv_id(); + + my $minimum_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_minimum' + })->cvterm_id(); + + my $maximum_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_maximum' + })->cvterm_id(); + + my $format_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_format' + })->cvterm_id(); + + my $default_value_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_default_value' + })->cvterm_id(); + + my $categories_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_categories' + })->cvterm_id(); + + my $repeat_type_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_repeat_type' + })->cvterm_id(); + + my $category_details_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_details' + })->cvterm_id(); + + my %cvtermprop_hash = ( + "$format_cvterm_id" => $format, + "$default_value_cvterm_id" => $default_value, + "$minimum_cvterm_id" => $minimum, + "$maximum_cvterm_id" => $maximum, + "$categories_cvterm_id" => $categories, + "$repeat_type_cvterm_id" => $repeat_type, + "$category_details_cvterm_id" => $category_details + ); + + my $trait_ontology_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + name => 'trait_ontology' + })->cvterm_id(); + my $trait_cv_id = $schema->resultset("Cv::Cvprop")->find({ + type_id => $trait_ontology_cvterm_id + })->cv_id(); + my $trait_ontology = $schema->resultset("Cv::Cv")->find({ + cv_id => $trait_cv_id + })->name(); + + my $get_db_from_cv_sql = "SELECT DISTINCT(db.name) FROM cvterm + JOIN dbxref USING (dbxref_id) + JOIN db USING (db_id) + WHERE cv_id=?"; + + my $h = $schema->storage->dbh->prepare($get_db_from_cv_sql); + $h->execute($trait_cv_id); + + my $db_name = $h->fetchrow_array(); + + if ($parent_term) { + my $lt = CXGN::List::Transform->new(); + + my $transform = $lt->transform($schema, "traits_2_trait_ids", [$parent_term]); + + if (@{$transform->{missing}}>0) { + die "Parent term $parent_term could not be found in the database.\n"; + } + + my @parent_id_list = @{$transform->{transform}}; + $parent_id = $parent_id_list[0]; + } else { + my $ontology_obj = CXGN::Onto->new({ + schema => $schema + }); + my @root_nodes = $ontology_obj->get_root_nodes('trait_ontology'); + + my $root_term_name = $root_nodes[0]->[1] =~ s/\w+:\d+ //r; + + $parent_id = $schema->resultset("Cv::Cvterm")->find({ + name => $root_term_name, + cv_id => $root_nodes[0]->[0] + })->cvterm_id(); + } + + my $get_db_accessions_sql = "SELECT accession FROM dbxref JOIN db USING (db_id) WHERE db.name=?;"; + + my $relationship_cv = $schema->resultset("Cv::Cv")->find({ name => 'relationship'}); + my $rel_cv_id; + if ($relationship_cv) { + $rel_cv_id = $relationship_cv->cv_id ; + } else { + die "No relationship ontology in DB.\n"; + } + my $variable_relationship = $schema->resultset("Cv::Cvterm")->find({ name => 'VARIABLE_OF' , cv_id => $rel_cv_id }); + my $variable_of_id; + if ($variable_relationship) { + $variable_of_id = $variable_relationship->cvterm_id(); + } + my $isa_relationship = $schema->resultset("Cv::Cvterm")->find({ name => 'is_a' , cv_id => $rel_cv_id }); + my $isa_id; + if ($isa_relationship) { + $isa_id = $isa_relationship->cvterm_id(); + } + + $h = $schema->storage->dbh->prepare($get_db_accessions_sql); + $h->execute($db_name); + + my @accessions; + + while (my $accession = $h->fetchrow_array()) { + push @accessions, int($accession =~ s/^0+//r); + } + + my $accession_num = max(@accessions) + 1; + my $zeroes = "0" x (7-length($accession_num)); + + my $new_trait_id; + my $new_trait; + + my $coderef = sub { + eval { + $new_trait_id = $schema->resultset("Cv::Cvterm")->create_with({ + name => $name, + cv => $trait_ontology, + db => $db_name, + dbxref => "$zeroes"."$accession_num" + })->cvterm_id(); + + if ($format eq "ontology") { + $schema->resultset("Cv::CvtermRelationship")->find_or_create({ + object_id => $parent_id, + subject_id => $new_trait_id, + type_id => $isa_id + }); + } else { + $schema->resultset("Cv::CvtermRelationship")->find_or_create({ + object_id => $parent_id, + subject_id => $new_trait_id, + type_id => $variable_of_id + }); + } + + $new_trait = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_cv_id, + cvterm_id => $new_trait_id, + name => $name + }); + $new_trait->definition($definition); + $new_trait->update(); + + foreach my $cvtermprop (keys(%cvtermprop_hash)) { + if (defined($cvtermprop_hash{$cvtermprop})) { + $schema->resultset("Cv::Cvtermprop")->find_or_create({ + cvterm_id => $new_trait_id, + type_id => $cvtermprop, + value => $cvtermprop_hash{$cvtermprop}, + rank => 0 + }); + } + } + }; + if ($@) { + $schema->txn_rollback(); + die "An error occurred! $@"; + } else { + $schema->txn_commit(); + } + }; + + eval { + $schema->txn_do($coderef); + }; + if ($@) { + die "An error occurred saving trait: $@\n"; + } + + $self->cvterm($new_trait); + $self->cvterm_id($new_trait_id); + $self->dbxref_id($new_trait->dbxref_id); + + return $new_trait; +} + +sub interactive_update { + my $self = shift; + my $schema = $self->bcs_schema(); + my $cvterm_id = $self->cvterm_id(); + my $definition = $self->definition(); + my $name = $self->name(); + + my $coderef = sub { + my $update_cvterm_sql = "UPDATE cvterm SET name = ? , definition = ? WHERE cvterm_id=?"; + + my $h = $schema->storage->dbh->prepare($update_cvterm_sql); + $h->execute($name, $definition, $cvterm_id); + }; + + $schema->txn_do($coderef); +} + +sub delete { + my $self = shift; + + my $schema = $self->bcs_schema; + my $cvterm_id = $self->cvterm_id; + my $name = $self->name; + + my $check_phenotypes_q = "SELECT COUNT(*) FROM phenotype WHERE cvalue_id=?"; + + my $h = $schema->storage->dbh->prepare($check_phenotypes_q); + $h->execute($cvterm_id); + + my $phenotype_count = $h->fetchrow_array(); + + if ($phenotype_count > 0) { + die "Cannot delete cvterm $name because it has associated phenotypes. Delete these phenotypes before attempting to delete the cvterm.\n"; + } + + if (!$self->db) { + die "It appears this cvterm is not part of an ontology. Deleting it is likely unsafe.\n"; + } + + my $coderef = sub { + $schema->resultset("Cv::Cvterm")->find({ + cvterm_id => $cvterm_id + })->delete(); + + $schema->resultset("General::Dbxref")->find({ + dbxref_id => $self->dbxref_id + })->delete(); + }; + + $schema->txn_do($coderef); + + return 1; +} + sub _fetch_synonyms { my $self = shift; my @synonyms = (); diff --git a/lib/CXGN/Trait/Treatment.pm b/lib/CXGN/Trait/Treatment.pm new file mode 100644 index 0000000000..4633dba74f --- /dev/null +++ b/lib/CXGN/Trait/Treatment.pm @@ -0,0 +1,224 @@ +=head1 NAME + +CXGN::Trait::Treatment - an instance of CXGN::Trait that always uses EXPERIMENT_TREATMENT for ontology db.name + +=head1 DESCRIPTION + +This module contains functions for creating and storing experimental treatments that can be created by curators. +Treatments are stored and applied in the same way as phenotypes, though with a different ontology. The only +differences between traits and treatments in their implementation is that treatments are always part of the +experiment_treatment_ontology, with db.name EXPERIMENT_TREATMENT. All other behavior is essentially the same, +though this module contains functions for the creation of treatments that are not available to traits. + +=head1 SYNOPSIS + +my $treatment = CXGN::Trait::Treatment->new({ + bcs_schema => $schema, + definition => $definition, + name => $name, + format => $format +}); + +=head1 AUTHOR + +Ryan Preble + +=cut + +package CXGN::Trait::Treatment; + +use Moose; +use Data::Dumper; +use List::Util qw(max); +use CXGN::List::Transform; +use JSON; + +BEGIN {extends 'CXGN::Trait'}; + +sub BUILD { + my $self = shift; + $self->db('EXPERIMENT_TREATMENT'); +} + +sub store { + my $self = shift; + my $parent_term = shift; + + my $schema = $self->bcs_schema(); + + my $lt = CXGN::List::Transform->new(); + + my $transform = $lt->transform($schema, "traits_2_trait_ids", [$parent_term]); + + if (@{$transform->{missing}}>0) { + die "Parent term $parent_term could not be found in the database.\n"; + } + + my @parent_id_list = @{$transform->{transform}}; + my $parent_id = $parent_id_list[0]; + + my $name = $self->name() || die "No name found.\n"; + my $definition = $self->definition() || die "No definition found.\n"; + my $format = $self->format() || die "No format found.\n"; + my $default_value = $self->default_value() ne "" ? $self->default_value() : undef; + my $minimum = $self->minimum() ne "" ? $self->minimum() : undef; + my $maximum = $self->maximum() ne "" ? $self->maximum() : undef; + my $categories = $self->categories() ne "" ? $self->categories() : undef; + my $repeat_type = $self->repeat_type() ne "" ? $self->repeat_type() : undef; + my $category_details = $self->category_details() ne "" ? $self->category_details() : undef; + + my $trait_property_cv_id = $schema->resultset("Cv::Cv")->find({name => 'trait_property'})->cv_id(); + + my $minimum_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_minimum' + })->cvterm_id(); + + my $maximum_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_maximum' + })->cvterm_id(); + + my $format_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_format' + })->cvterm_id(); + + my $default_value_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_default_value' + })->cvterm_id(); + + my $categories_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_categories' + })->cvterm_id(); + + my $repeat_type_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_repeat_type' + })->cvterm_id(); + + my $category_details_cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $trait_property_cv_id, + name => 'trait_details' + })->cvterm_id(); + + my %cvtermprop_hash = ( + "$format_cvterm_id" => $format, + "$default_value_cvterm_id" => $default_value, + "$minimum_cvterm_id" => $minimum, + "$maximum_cvterm_id" => $maximum, + "$categories_cvterm_id" => $categories, + "$repeat_type_cvterm_id" => $repeat_type, + "$category_details_cvterm_id" => $category_details + ); + + my $get_db_accessions_sql = "SELECT accession FROM dbxref JOIN db USING (db_id) WHERE db.name='EXPERIMENT_TREATMENT';"; + + my $relationship_cv = $schema->resultset("Cv::Cv")->find({ name => 'relationship'}); + my $rel_cv_id; + if ($relationship_cv) { + $rel_cv_id = $relationship_cv->cv_id ; + } else { + die "No relationship ontology in DB.\n"; + } + my $variable_relationship = $schema->resultset("Cv::Cvterm")->find({ name => 'VARIABLE_OF' , cv_id => $rel_cv_id }); + my $variable_of_id; + if ($variable_relationship) { + $variable_of_id = $variable_relationship->cvterm_id(); + } + my $isa_relationship = $schema->resultset("Cv::Cvterm")->find({ name => 'is_a' , cv_id => $rel_cv_id }); + my $isa_id; + if ($isa_relationship) { + $isa_id = $isa_relationship->cvterm_id(); + } + + my $experiment_treatment_cv = $schema->resultset("Cv::Cv")->find({ name => 'experiment_treatment'}); + my $experiment_treatment_cv_id; + if ($experiment_treatment_cv) { + $experiment_treatment_cv_id = $experiment_treatment_cv->cv_id ; + } else { + die "No experiment_treatment CV found. Has DB patch been run?\n"; + } + + my $h = $schema->storage->dbh->prepare($get_db_accessions_sql); + $h->execute(); + + my @accessions; + + while (my $accession = $h->fetchrow_array()) { + push @accessions, int($accession =~ s/^0+//r); + } + + my $accession_num = max(@accessions) + 1; + my $zeroes = "0" x (7-length($accession_num)); + + my $new_treatment_id; + my $new_treatment; + + my $coderef = sub { + eval { + $new_treatment_id = $schema->resultset("Cv::Cvterm")->create_with({ + name => $name, + cv => 'experiment_treatment', + db => 'EXPERIMENT_TREATMENT', + dbxref => "$zeroes"."$accession_num" + })->cvterm_id(); + + if ($format eq "ontology") { + $schema->resultset("Cv::CvtermRelationship")->find_or_create({ + object_id => $parent_id, + subject_id => $new_treatment_id, + type_id => $isa_id + }); + } else { + $schema->resultset("Cv::CvtermRelationship")->find_or_create({ + object_id => $parent_id, + subject_id => $new_treatment_id, + type_id => $variable_of_id + }); + } + + $new_treatment = $schema->resultset("Cv::Cvterm")->find({ + cv_id => $experiment_treatment_cv_id, + cvterm_id => $new_treatment_id, + name => $name + }); + $new_treatment->definition($definition); + $new_treatment->update(); + + foreach my $cvtermprop (keys(%cvtermprop_hash)) { + if (defined($cvtermprop_hash{$cvtermprop})) { + $schema->resultset("Cv::Cvtermprop")->find_or_create({ + cvterm_id => $new_treatment_id, + type_id => $cvtermprop, + value => $cvtermprop_hash{$cvtermprop}, + rank => 0 + }); + } + } + }; + if ($@) { + $schema->txn_rollback(); + die "An error occurred! $@"; + } else { + $schema->txn_commit(); + } + }; + + eval { + $schema->txn_do($coderef); + }; + if ($@) { + die "An error occurred saving treatment: $@\n"; + } + + $self->cvterm($new_treatment); + $self->cvterm_id($new_treatment_id); + $self->dbxref_id($new_treatment->dbxref_id); + + return $new_treatment; +} + +1; \ No newline at end of file diff --git a/lib/CXGN/Trial/ParseUpload/Plugin/MultipleTrialDesignGeneric.pm b/lib/CXGN/Trial/ParseUpload/Plugin/MultipleTrialDesignGeneric.pm index d90db7837b..19ed41f16b 100644 --- a/lib/CXGN/Trial/ParseUpload/Plugin/MultipleTrialDesignGeneric.pm +++ b/lib/CXGN/Trial/ParseUpload/Plugin/MultipleTrialDesignGeneric.pm @@ -5,13 +5,15 @@ use List::MoreUtils qw(uniq); use CXGN::File::Parse; use SGN::Model::Cvterm; use CXGN::List::Validate; +use CXGN::List::Transform; use CXGN::Stock::Seedlot; use CXGN::Calendar; use CXGN::Trial; +use CXGN::Trait; my @REQUIRED_COLUMNS = qw|trial_name breeding_program location year design_type description accession_name plot_number block_number|; my @OPTIONAL_COLUMNS = qw|plot_name trial_type trial_stock_type plot_width plot_length field_size planting_date transplanting_date harvest_date is_a_control rep_number range_number row_number col_number seedlot_name num_seed_per_plot weight_gram_seed_per_plot entry_number|; -# Any additional columns that are not required or optional will be used as a treatment +# Any additional columns that are not required or optional will be parsed as treatments. # VALID DESIGN TYPES my %valid_design_types = ( @@ -75,6 +77,22 @@ sub _validate_with_plugin { my $parsed_values = $parsed->{'values'}; my $treatments = $parsed->{'additional_columns'}; + my $trait_validator = CXGN::List::Validate->new(); + + my $validate = $trait_validator->validate($schema, "traits", $treatments); + + foreach my $treatment (@{$treatments}) { + if ($treatment !~ m/_TREATMENT:/) { + push @error_messages, "Column $treatment is not formatted like a treatment. Use only full, valid treatment names.\n"; + } + } + + if (@{$validate->{missing}}>0) { + foreach my $missing (@{$validate->{missing}}) { + push @error_messages, "Treatment $missing does not exist in the database.\n"; + } + } + # Return file parsing errors if ( $parsed_errors && scalar(@$parsed_errors) > 0 ) { $errors{'error_messages'} = $parsed_errors; @@ -123,6 +141,32 @@ sub _validate_with_plugin { my $weight_gram_seed_per_plot = $data->{'weight_gram_seed_per_plot'}; my $entry_number = $data->{'entry_number'}; + foreach my $treatment (@{$treatments}) { + my $lt = CXGN::List::Transform->new(); + + my $transform = $lt->transform($schema, 'traits_2_trait_ids', [$treatment]); + my @treatment_id_list = @{$transform->{transform}}; + my $treatment_id = $treatment_id_list[0]; + + my $treatment_obj = CXGN::Trait->new({ + bcs_schema => $schema, + cvterm_id => $treatment_id + }); + if ($treatment_obj->format() eq "numeric" && defined($treatment_obj->minimum()) && defined($data->{$treatment}) && $data->{$treatment} < $treatment_obj->minimum()) { + push @error_messages, "Row $row: value for $treatment is lower than the allowed minimum for that treatment."; + } + if ($treatment_obj->format() eq "numeric" && defined($treatment_obj->maximum()) && defined($data->{$treatment}) && $data->{$treatment} > $treatment_obj->maximum()) { + push @error_messages, "Row $row: value for $treatment is higher than the allowed maximum for that treatment."; + } + if ($treatment_obj->format() eq "qualitative" && defined($treatment_obj->categories()) && defined($data->{$treatment})) { + my $qual_value = $data->{$treatment}; + my $categories = $treatment_obj->categories(); + if ( $categories !~ m/$qual_value/) { + push @error_messages, "Row $row: value for $treatment is not in the valid categories for that treatment."; + } + } + } + # Plot Number: must be a positive number if (!($plot_number =~ /^\d+?$/)) { push @error_messages, "Row $row: plot number $plot_number must be a positive integer."; @@ -205,15 +249,6 @@ sub _validate_with_plugin { push @error_messages, "Row $row: entry_number $entry_number must be a positive integer."; } - # Treatment Values: must be either blank, 0, or 1 - foreach my $treatment (@$treatments) { - my $treatment_value = $data->{$treatment}; - if ( $treatment_value && $treatment_value ne '' && $treatment_value ne '0' && $treatment_value ne '1' ) { - push @error_messages, "Row $row: Treatment value for treatment $treatment should be either 1 (applied) or empty (not applied)."; - } - } - - # Create maps to check for overall validation within individual trials my $tk = $trial_name; @@ -599,13 +634,6 @@ sub _parse_with_plugin { $seen_entry_numbers{$current_trial_name}->{$accession_name} = $entry_number; } - foreach my $treatment_name (@$treatments){ - my $treatment_value = $row->{$treatment_name}; - if ( $treatment_value ) { - push @{$design_details{treatments}->{$treatment_name}{new_treatment_stocks}}, $plot_name; - } - } - if ($acc_synonyms_lookup{$accession_name}){ my @accession_names = keys %{$acc_synonyms_lookup{$accession_name}}; if (scalar(@accession_names)>1){ @@ -641,6 +669,11 @@ sub _parse_with_plugin { $design_details{$key}->{num_seed_per_plot} = $num_seed_per_plot; $design_details{$key}->{weight_gram_seed_per_plot} = $weight_gram_seed_per_plot; } + foreach my $treatment (@{$treatments}) { + if (defined($row->{$treatment})) { + $design_details{'treatments'}->{$plot_name}->{$treatment} = [$row->{$treatment}]; + } + } } # add last trial design to all_designs and save parsed data, then return diff --git a/lib/CXGN/Trial/ParseUpload/Plugin/TrialGeneric.pm b/lib/CXGN/Trial/ParseUpload/Plugin/TrialGeneric.pm index 7c9a914f91..a48bdc6d11 100644 --- a/lib/CXGN/Trial/ParseUpload/Plugin/TrialGeneric.pm +++ b/lib/CXGN/Trial/ParseUpload/Plugin/TrialGeneric.pm @@ -5,8 +5,10 @@ use List::MoreUtils qw(uniq); use CXGN::File::Parse; use SGN::Model::Cvterm; use CXGN::List::Validate; +use CXGN::List::Transform; use CXGN::Stock::Seedlot; use CXGN::Trial; +use Data::Dumper; my @REQUIRED_COLUMNS = qw|stock_name plot_number block_number|; # stock_name can also be accession_name, cross_unique_id, or family_name @@ -41,6 +43,22 @@ sub _validate_with_plugin { my $parsed_values = $parsed->{'values'}; my $treatments = $parsed->{'additional_columns'}; + my $trait_validator = CXGN::List::Validate->new(); + + my $validate = $trait_validator->validate($schema, "traits", $treatments); + + foreach my $treatment (@{$treatments}) { + if ($treatment !~ m/_TREATMENT:/) { + push @error_messages, "Column $treatment is not formatted like a treatment. Use only full, valid treatment names.\n"; + } + } + + if (@{$validate->{missing}}>0) { + foreach my $missing (@{$validate->{missing}}) { + push @error_messages, "Treatment $missing does not exist in the database.\n"; + } + } + # Return file parsing errors if ( $parsed_errors && scalar(@$parsed_errors) > 0 ) { $errors{'error_messages'} = $parsed_errors; @@ -83,6 +101,32 @@ sub _validate_with_plugin { my $weight_gram_seed_per_plot = $data->{'weight_gram_seed_per_plot'}; my $entry_number = $data->{'entry_number'}; + foreach my $treatment (@{$treatments}) { + my $lt = CXGN::List::Transform->new(); + + my $transform = $lt->transform($schema, 'traits_2_trait_ids', [$treatment]); + my @treatment_id_list = @{$transform->{transform}}; + my $treatment_id = $treatment_id_list[0]; + + my $treatment_obj = CXGN::Trait->new({ + bcs_schema => $schema, + cvterm_id => $treatment_id + }); + if ($treatment_obj->format() eq "numeric" && defined($treatment_obj->minimum()) && defined($data->{$treatment}) && $data->{$treatment} < $treatment_obj->minimum()) { + push @error_messages, "Row $row: value for $treatment is lower than the allowed minimum for that treatment."; + } + if ($treatment_obj->format() eq "numeric" && defined($treatment_obj->maximum()) && defined($data->{$treatment}) && $data->{$treatment} > $treatment_obj->maximum()) { + push @error_messages, "Row $row: value for $treatment is higher than the allowed maximum for that treatment."; + } + if ($treatment_obj->format() eq "qualitative" && defined($treatment_obj->categories()) && defined($data->{$treatment})) { + my $qual_value = $data->{$treatment}; + my $categories = $treatment_obj->categories(); + if ( $categories !~ m/$qual_value/) { + push @error_messages, "Row $row: value for $treatment is not in the valid categories for that treatment."; + } + } + } + # Plot Number: must be a positive number if (!($plot_number =~ /^\d+?$/)) { @@ -155,14 +199,6 @@ sub _validate_with_plugin { push @error_messages, "Row $row: entry_number $entry_number must be a positive integer."; } - # Treatment Values: must be either blank, 0, or 1 - foreach my $treatment (@$treatments) { - my $treatment_value = $data->{$treatment}; - if ( $treatment_value && $treatment_value ne '' && $treatment_value ne '0' && $treatment_value ne '1' ) { - push @error_messages, "Row $row: Treatment value for treatment $treatment should be either 1 (applied) or empty (not applied)."; - } - } - # Map to check for duplicated plot numbers if ( $plot_number ) { @@ -358,6 +394,7 @@ sub _parse_with_plugin { # Build trial design my %design; my %seen_entry_numbers; + my $treatment_design; foreach (@$data) { my $r = $_; my $row = $r->{'_row'}; @@ -379,13 +416,6 @@ sub _parse_with_plugin { my $weight_gram_seed_per_plot = $r->{'weight_gram_seed_per_plot'} || 0; my $entry_number = $r->{'entry_number'}; - foreach my $treatment_name (@$treatments) { - my $treatment_value = $r->{$treatment_name}; - if ($treatment_value) { - push @{$design{treatments}->{$treatment_name}{new_treatment_stocks}}, $plot_name; - } - } - if ($stock_synonyms_lookup{$stock_name}) { my @stock_names = keys %{$stock_synonyms_lookup{$stock_name}}; if (scalar(@stock_names)>1) { @@ -424,12 +454,19 @@ sub _parse_with_plugin { $design{$row}->{num_seed_per_plot} = $num_seed_per_plot; $design{$row}->{weight_gram_seed_per_plot} = $weight_gram_seed_per_plot; } + foreach my $treatment (@{$treatments}) { + if (defined($r->{$treatment})) { + $treatment_design->{$plot_name}->{$treatment} = $r->{$treatment}; + } + } } my %parsed_data = ( design => \%design, - entry_numbers => \%seen_entry_numbers + entry_numbers => \%seen_entry_numbers, + treatment_design => $treatment_design ); + $self->_set_parsed_data(\%parsed_data); return 1; diff --git a/lib/CXGN/Trial/ParseUpload/Plugin/TrialPlantsSubplotWithNumberOfPlantsXLS.pm b/lib/CXGN/Trial/ParseUpload/Plugin/TrialPlantsSubplotWithNumberOfPlantsXLS.pm index c4bb798cdf..03e9f8f370 100644 --- a/lib/CXGN/Trial/ParseUpload/Plugin/TrialPlantsSubplotWithNumberOfPlantsXLS.pm +++ b/lib/CXGN/Trial/ParseUpload/Plugin/TrialPlantsSubplotWithNumberOfPlantsXLS.pm @@ -109,10 +109,10 @@ sub _validate_with_plugin { $coords_given = 1; } if ($coords_given && (!$num_rows || !$num_cols)) { - push @error_messages, "Number of rows and columns given, but missing for plot $plot_name."; + push @error_messages, "Number of rows and columns given, but missing for plot $subplot_name."; } - if($coords_given && $num_rows * $num_cols < $num_plants_per_plot) { - push @error_messages, "Number of rows and columns not sufficient for the number of plants in $plot_name."; + if($coords_given && $num_rows * $num_cols < $num_plants_per_subplot) { + push @error_messages, "Number of rows and columns not sufficient for the number of plants in $subplot_name."; } if (!$subplot_name || $subplot_name eq '' ) { diff --git a/lib/CXGN/Trial/TrialCreate.pm b/lib/CXGN/Trial/TrialCreate.pm index b236906303..0bdbb26eca 100644 --- a/lib/CXGN/Trial/TrialCreate.pm +++ b/lib/CXGN/Trial/TrialCreate.pm @@ -472,8 +472,6 @@ sub save_trial { is_genotyping => $self->get_is_genotyping(), is_analysis => $self->get_is_analysis(), is_sampling_trial => $self->get_is_sampling_trial(), - new_treatment_has_plant_entries => $self->get_trial_has_plant_entries, - new_treatment_has_subplot_entries => $self->get_trial_has_subplot_entries, operator => $self->get_operator, trial_stock_type => $self->get_trial_stock_type(), }); @@ -491,7 +489,7 @@ sub save_trial { }; } if ($error) { - return { error => $error }; + return { error => $error }; } return { trial_id => $project->project_id }; } diff --git a/lib/CXGN/Trial/TrialDesign.pm b/lib/CXGN/Trial/TrialDesign.pm index b2661e32ef..1329f7ac61 100644 --- a/lib/CXGN/Trial/TrialDesign.pm +++ b/lib/CXGN/Trial/TrialDesign.pm @@ -98,7 +98,7 @@ has 'fieldmap_row_number' => (isa => 'Int',is => 'rw',predicate => 'has_fieldmap has 'plot_layout_format' => (isa => 'Str', is => 'rw', predicate => 'has_plot_layout_format', clearer => 'clear_plot_layout_format'); -has 'treatments' => (isa => 'ArrayRef', is => 'rw', predicate => 'has_treatments', clearer => 'clear_treatments'); +has 'treatments' => (isa => 'HashRef', is => 'rw', predicate => 'has_treatments', clearer => 'clear_treatments'); has 'num_plants_per_plot' => (isa => 'Int',is => 'rw',predicate => 'has_num_plants_per_plot',clearer => 'clear_num_plants_per_plot'); diff --git a/lib/CXGN/Trial/TrialDesign/Plugin/splitplot.pm b/lib/CXGN/Trial/TrialDesign/Plugin/splitplot.pm index 7c645d31e6..0977077e17 100644 --- a/lib/CXGN/Trial/TrialDesign/Plugin/splitplot.pm +++ b/lib/CXGN/Trial/TrialDesign/Plugin/splitplot.pm @@ -2,6 +2,7 @@ package CXGN::Trial::TrialDesign::Plugin::splitplot; use Moose::Role; +use Data::Dumper; sub create_design { my $self = shift; @@ -15,7 +16,6 @@ sub create_design { my $result_matrix; my @plot_numbers; my @subplots_numbers; - my @treatments; my @stock_names; my @block_numbers; my @rep_numbers; @@ -27,6 +27,7 @@ sub create_design { my $plot_layout_format; my @col_number_fieldmaps; my $treatments; + my @unique_treatments = (); my $num_plants_per_plot; if ($self->has_stock_list()) { @stock_list = @{$self->get_stock_list()}; @@ -35,7 +36,27 @@ sub create_design { die "No stock list specified\n"; } if ($self->has_treatments()) { - $treatments = $self->get_treatments(); + $treatments = $self->get_treatments(); #at this point this is a hashref of arrayrefs. Each hash key is a treatment name, and the arrays are the values of that treatment to be applied. + my @treatment_names = keys(%{$treatments}); + + my @aggregator = (); + foreach my $treatment (@treatment_names) { + my @formatted_values = map {"{$treatment=$_}"} @{$treatments->{$treatment}}; + if (scalar(@aggregator) == 0) { #aggregator is empty + @aggregator = @formatted_values; + } else { #aggregator is not empty + my @new_aggregator = (); + foreach my $val (@aggregator) { + foreach my $new_val (@formatted_values) { + push @new_aggregator, $val.$new_val + } + } + @aggregator = @new_aggregator; + } + } + + @unique_treatments = @aggregator; + } else { die "treatments not specified\n"; } @@ -69,13 +90,12 @@ sub create_design { data => \@stock_list, }); #print STDERR Dumper $stock_data_matrix; - my $treatment_data_matrix = R::YapRI::Data::Matrix->new({ + my $treatment_data_matrix = R::YapRI::Data::Matrix->new({ #TODO figure out if this is right name => 'treatment_data_matrix', rown => 1, - coln => scalar(@$treatments), - data => $treatments, + coln => scalar(@unique_treatments), + data => \@unique_treatments, }); - #print STDERR Dumper $treatment_data_matrix; $r_block = $rbase->create_block('r_block'); $stock_data_matrix->send_rbase($rbase, 'r_block'); @@ -104,7 +124,8 @@ sub create_design { @subplots_numbers = $result_matrix->get_column("splots"); @rep_numbers = $result_matrix->get_column("block"); @stock_names = $result_matrix->get_column("accessions"); - @treatments = $result_matrix->get_column("treatments"); + my @treatments = $result_matrix->get_column("treatments"); + @converted_plot_numbers = @plot_numbers; my %subplot_plots; @@ -128,33 +149,36 @@ sub create_design { $plot_info{'plot_number'} = $converted_plot_numbers[$i]; push @{$subplot_plots{$converted_plot_numbers[$i]}}, $subplots_numbers[$i]; $plot_info{'subplots_names'} = $subplot_plots{$converted_plot_numbers[$i]}; - push @{$treatment_plots{$converted_plot_numbers[$i]}}, $treatments[$i]; - $plot_info{'treatments'} = $treatment_plots{$converted_plot_numbers[$i]}; $splitplot_design{$converted_plot_numbers[$i]} = \%plot_info; } %splitplot_design = %{$self->_build_plot_names(\%splitplot_design)}; + my $subplot_plant_dictionary; while(my($plot,$val) = each(%splitplot_design)){ my $subplots = $val->{'subplots_names'}; - my $treatments = $val->{'treatments'}; my $num_plants_per_subplot = $num_plants_per_plot/scalar(@$subplots); my %subplot_plants_hash; my @plant_names; my $plant_index = 1; for(my $i=0; $i[$i]}}, $subplots->[$i]; + push @{$treatment_subplot_hash{$treatments[$i]}}, $subplots->[$i]; + my @subplot_plant_names; for(my $j=0; $j<$num_plants_per_subplot; $j++){ my $plant_name = $subplots->[$i]."_plant_$plant_index"; push @{$subplot_plants_hash{$subplots->[$i]}}, $plant_name; - push @{$treatment_subplot_hash{$treatments->[$i]}}, $plant_name; push @plant_names, $plant_name; + push @subplot_plant_names, $plant_name; $plant_index++; } + $subplot_plant_dictionary->{$subplots->[$i]} = [@subplot_plant_names]; } $val->{plant_names} = \@plant_names; $val->{subplots_plant_names} = \%subplot_plants_hash; } - $splitplot_design{'treatments'} = \%treatment_subplot_hash; + $splitplot_design{'treatments'} = { + plants => $subplot_plant_dictionary, + treatments => \%treatment_subplot_hash + }; return \%splitplot_design; } diff --git a/lib/CXGN/Trial/TrialDesignStore.pm b/lib/CXGN/Trial/TrialDesignStore.pm index 8f1b424914..21b6e2783e 100644 --- a/lib/CXGN/Trial/TrialDesignStore.pm +++ b/lib/CXGN/Trial/TrialDesignStore.pm @@ -76,8 +76,7 @@ To add new treatments, There should be a key in the design called "treatments" s { "treatments" => { - "fertilizer_10ml" => ["plot1", "plot2", "plot1_plant1", "plot2_plant1"], - "water" => ["plot1", "plot2"] + "treatment_name|EXPERIMENT_TREATMENT0000001" => ["plot1", "plot2", "plot1_plant1", "plot2_plant1"] } } diff --git a/lib/CXGN/Trial/TrialDesignStore/AbstractTrial.pm b/lib/CXGN/Trial/TrialDesignStore/AbstractTrial.pm index f33cd2cc52..e1dc5c2030 100644 --- a/lib/CXGN/Trial/TrialDesignStore/AbstractTrial.pm +++ b/lib/CXGN/Trial/TrialDesignStore/AbstractTrial.pm @@ -109,54 +109,54 @@ If the stocks in the design were checked and whether the check passed. Internal has 'stocks_exist' => (isa => 'Bool', is => 'rw', required => 0, default => 0); -=head2 set_new_treatment_has_plant_entries(), get_new_treatment_has_plant_entries() +# =head2 set_new_treatment_has_plant_entries(), get_new_treatment_has_plant_entries() -Whether this treatment has plant entries. +# Whether this treatment has plant entries. -=cut +# =cut -has 'new_treatment_has_plant_entries' => (isa => 'Maybe[Int]', is => 'rw', required => 0, default => 0); +# has 'new_treatment_has_plant_entries' => (isa => 'Maybe[Int]', is => 'rw', required => 0, default => 0); -=head2 set_new_treatment_has_subplot_entries(), get_new_treatment_has_subplot_entries() +# =head2 set_new_treatment_has_subplot_entries(), get_new_treatment_has_subplot_entries() -Whether this treatment has subplot entries. +# Whether this treatment has subplot entries. -=cut +# =cut -has 'new_treatment_has_subplot_entries' => (isa => 'Maybe[Int]', is => 'rw', required => 0, default => 0); +# has 'new_treatment_has_subplot_entries' => (isa => 'Maybe[Int]', is => 'rw', required => 0, default => 0); -=head2 set_new_treatment_has_tissue_sample_entries(), get_new_treatment_has_tissue_sample_entries() +# =head2 set_new_treatment_has_tissue_sample_entries(), get_new_treatment_has_tissue_sample_entries() -Whether this treatment has tissue_sample entries. +# Whether this treatment has tissue_sample entries. -=cut +# =cut -has 'new_treatment_has_tissue_sample_entries' => (isa => 'Maybe[Int]', is => 'rw', required => 0, default => 0); +# has 'new_treatment_has_tissue_sample_entries' => (isa => 'Maybe[Int]', is => 'rw', required => 0, default => 0); -=head2 set_new_treatment_date, get_new_treatment_date() +# =head2 set_new_treatment_date, get_new_treatment_date() -A new treatment date. +# A new treatment date. -=cut +# =cut -has 'new_treatment_date' => (isa => 'Maybe[Str]', is => 'rw', required => 0, default => 0); +# has 'new_treatment_date' => (isa => 'Maybe[Str]', is => 'rw', required => 0, default => 0); -=head2 set_new_treatment_year, get_new_treatment_year() +# =head2 set_new_treatment_year, get_new_treatment_year() -A new treatment year. +# A new treatment year. -=cut +# =cut -has 'new_treatment_year' => (isa => 'Maybe[Str]', is => 'rw', required => 0, default => 0); +# has 'new_treatment_year' => (isa => 'Maybe[Str]', is => 'rw', required => 0, default => 0); -=head2 set_new_treatment_type, get_new_treatment_type() +# =head2 set_new_treatment_type, get_new_treatment_type() -A new treatment type. +# A new treatment type. -=cut +# =cut -has 'new_treatment_type' => (isa => 'Maybe[Str]', is => 'rw', required => 0, default => 0); +# has 'new_treatment_type' => (isa => 'Maybe[Str]', is => 'rw', required => 0, default => 0); =head2 set_operator(), get_operator() @@ -282,7 +282,7 @@ has 'ncbi_taxonomy_id_cvterm_id' => (isa => 'Int', is => 'rw'); has 'notes_cvterm_id' => (isa => 'Int', is => 'rw'); -has 'treatment_nd_experiment_type_id' => (isa => 'Int', is => 'rw'); +# has 'treatment_nd_experiment_type_id' => (isa => 'Int', is => 'rw'); has 'project_design_cvterm_id' => (isa => 'Int', is => 'rw'); @@ -292,7 +292,7 @@ has 'management_factor_date_cvterm_id' => (isa => 'Int', is => 'rw'); has 'management_factor_type_cvterm_id' => (isa => 'Int', is => 'rw'); -has 'trial_treatment_relationship_cvterm_id' => (isa => 'Int', is => 'rw'); +# has 'trial_treatment_relationship_cvterm_id' => (isa => 'Int', is => 'rw'); has 'has_plants_cvterm' => (isa => 'Int', is => 'rw'); @@ -372,7 +372,7 @@ sub BUILD { $self->set_notes_cvterm_id(SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'notes', 'stock_property')->cvterm_id()); - $self->set_treatment_nd_experiment_type_id(SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id()); + # $self->set_treatment_nd_experiment_type_id(SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'treatment_experiment', 'experiment_type')->cvterm_id()); $self->set_project_design_cvterm_id(SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'design', 'project_property')->cvterm_id()); @@ -382,7 +382,7 @@ sub BUILD { $self->set_management_factor_type_cvterm_id(SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'management_factor_type', 'project_property')->cvterm_id()); - $self->set_trial_treatment_relationship_cvterm_id(SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'trial_treatment_relationship', 'project_relationship')->cvterm_id()); + # $self->set_trial_treatment_relationship_cvterm_id(SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'trial_treatment_relationship', 'project_relationship')->cvterm_id()); $self->set_has_plants_cvterm(SGN::Model::Cvterm->get_cvterm_row($chado_schema, 'project_has_plant_entries', 'project_property')->cvterm_id()); @@ -959,7 +959,7 @@ sub store { ); - my $plant = $stock_rs->create({ + my $plant = $stock_rs->find_or_create({ organism_id => $organism_id_checked, name => $plant_name, uniquename => $plant_name, @@ -1099,7 +1099,7 @@ sub store { } } - my $subplot = $stock_rs->create({ + my $subplot = $stock_rs->find_or_create({ organism_id => $organism_id_checked, name => $subplot_name, uniquename => $subplot_name, @@ -1116,105 +1116,105 @@ sub store { } } - if (exists($design{'treatments'})){ - print STDERR "Saving treatments\n"; - my %treatments_hash = %{$design{'treatments'}}; - - foreach my $treatment_name (sort keys %treatments_hash) { - my $stock_names; - my $management_factor_type; - my $management_factor_year; - my $management_factor_date; - my $management_factor_description = ''; -# my %info_hashes = %{$treatments_hash{$treatment_name}}; - my $info_value = $treatments_hash{$treatment_name}; - my $info_type = ref($info_value); - if ($info_type eq 'HASH'){ - my %info_hashes = %$info_value; - $stock_names = $info_hashes{'new_treatment_stocks'}; - $management_factor_type = $info_hashes{'new_treatment_type'}; - $management_factor_year = $info_hashes{'new_treatment_year'}; - $management_factor_date = $info_hashes{'new_treatment_date'}; - $management_factor_description = $info_hashes{'new_treatment_description'} || 'No description'; - } else { - $stock_names = $treatments_hash{$treatment_name}; - } -# print STDERR "STOCK NAMES =".Dumper($stock_names)."\n"; - my @treatment_nd_experiment_stocks; - foreach (@$stock_names){ - my $stock_id; - if (exists($new_stock_ids_hash{$_})){ - $stock_id = $new_stock_ids_hash{$_}; - } else { - $stock_id = $chado_schema->resultset("Stock::Stock")->find({uniquename=>$_})->stock_id(); - } - push @treatment_nd_experiment_stocks, { type_id => $self->get_treatment_nd_experiment_type_id, stock_id => $stock_id }; - } - - my $nd_experiment = $chado_schema->resultset('NaturalDiversity::NdExperiment')->create({ - nd_geolocation_id => $nd_geolocation_id, - type_id => $self->get_treatment_nd_experiment_type_id, - nd_experiment_stocks => \@treatment_nd_experiment_stocks - }); - - my @treatment_project_props = ( - { type_id => $self->get_project_design_cvterm_id, value => 'treatment' } - ); - - if ($self->get_new_treatment_has_plant_entries){ - push @treatment_project_props, { type_id => $self->get_has_plants_cvterm, value => $self->get_new_treatment_has_plant_entries }; - } - if ($self->get_new_treatment_has_subplot_entries){ - push @treatment_project_props, { type_id => $self->get_has_subplots_cvterm, value => $self->get_new_treatment_has_subplot_entries }; - } - if ($self->get_new_treatment_has_tissue_sample_entries){ - push @treatment_project_props, { type_id => $self->get_has_tissues_cvterm, value => $self->get_new_treatment_has_tissue_sample_entries }; - } - if (defined $management_factor_type){ - push @treatment_project_props, { type_id => $self->get_management_factor_type_cvterm_id, value => $management_factor_type }; - } - if (defined $management_factor_year){ - push @treatment_project_props, { type_id => $self->get_management_factor_year_cvterm_id, value => $management_factor_year }; - } else { - my $t = CXGN::Trial->new({ - bcs_schema => $chado_schema, - trial_id => $self->get_trial_id - }); - push @treatment_project_props, { type_id => $self->get_management_factor_year_cvterm_id, value => $t->get_year() }; - } - - - if (defined $management_factor_date){ - push @treatment_project_props, { type_id => $self->get_management_factor_date_cvterm_id, value => $management_factor_date }; - } - - my @treatment_nd_experiment_project = ( - { nd_experiment_id => $nd_experiment->nd_experiment_id } - ); - - my @treatment_relationships = ( - { type_id => $self->get_trial_treatment_relationship_cvterm_id, object_project_id => $self->get_trial_id } - ); - - #Create a project for each treatment_name - my $project_treatment_name = $self->get_trial_name()."_".$treatment_name; - my $treatment_project = $chado_schema->resultset('Project::Project')->create({ - name => $project_treatment_name, - description => $management_factor_description, - projectprops => \@treatment_project_props, - project_relationship_subject_projects => \@treatment_relationships, - nd_experiment_projects => \@treatment_nd_experiment_project - }); - - if ($self->get_new_treatment_date()) { - my $management_factor_t = CXGN::Trial->new({ - bcs_schema => $chado_schema, - trial_id => $treatment_project->project_id() - }); - $management_factor_t->set_management_factor_date($self->get_new_treatment_date() ); - } - } - } +# if (exists($design{'treatments'})){ #TODO REFACTOR +# print STDERR "Saving treatments\n"; +# my %treatments_hash = %{$design{'treatments'}}; + +# foreach my $treatment_name (sort keys %treatments_hash) { +# my $stock_names; +# my $management_factor_type; +# my $management_factor_year; +# my $management_factor_date; +# my $management_factor_description = ''; +# # my %info_hashes = %{$treatments_hash{$treatment_name}}; +# my $info_value = $treatments_hash{$treatment_name}; +# my $info_type = ref($info_value); +# if ($info_type eq 'HASH'){ +# my %info_hashes = %$info_value; +# $stock_names = $info_hashes{'new_treatment_stocks'}; +# $management_factor_type = $info_hashes{'new_treatment_type'}; +# $management_factor_year = $info_hashes{'new_treatment_year'}; +# $management_factor_date = $info_hashes{'new_treatment_date'}; +# $management_factor_description = $info_hashes{'new_treatment_description'} || 'No description'; +# } else { +# $stock_names = $treatments_hash{$treatment_name}; +# } +# # print STDERR "STOCK NAMES =".Dumper($stock_names)."\n"; +# my @treatment_nd_experiment_stocks; +# foreach (@$stock_names){ +# my $stock_id; +# if (exists($new_stock_ids_hash{$_})){ +# $stock_id = $new_stock_ids_hash{$_}; +# } else { +# $stock_id = $chado_schema->resultset("Stock::Stock")->find({uniquename=>$_})->stock_id(); +# } +# push @treatment_nd_experiment_stocks, { type_id => $self->get_treatment_nd_experiment_type_id, stock_id => $stock_id }; +# } + +# my $nd_experiment = $chado_schema->resultset('NaturalDiversity::NdExperiment')->create({ +# nd_geolocation_id => $nd_geolocation_id, +# type_id => $self->get_treatment_nd_experiment_type_id, +# nd_experiment_stocks => \@treatment_nd_experiment_stocks +# }); + +# my @treatment_project_props = ( +# { type_id => $self->get_project_design_cvterm_id, value => 'treatment' } +# ); + +# if ($self->get_new_treatment_has_plant_entries){ +# push @treatment_project_props, { type_id => $self->get_has_plants_cvterm, value => $self->get_new_treatment_has_plant_entries }; +# } +# if ($self->get_new_treatment_has_subplot_entries){ +# push @treatment_project_props, { type_id => $self->get_has_subplots_cvterm, value => $self->get_new_treatment_has_subplot_entries }; +# } +# if ($self->get_new_treatment_has_tissue_sample_entries){ +# push @treatment_project_props, { type_id => $self->get_has_tissues_cvterm, value => $self->get_new_treatment_has_tissue_sample_entries }; +# } +# if (defined $management_factor_type){ +# push @treatment_project_props, { type_id => $self->get_management_factor_type_cvterm_id, value => $management_factor_type }; +# } +# if (defined $management_factor_year){ +# push @treatment_project_props, { type_id => $self->get_management_factor_year_cvterm_id, value => $management_factor_year }; +# } else { +# my $t = CXGN::Trial->new({ +# bcs_schema => $chado_schema, +# trial_id => $self->get_trial_id +# }); +# push @treatment_project_props, { type_id => $self->get_management_factor_year_cvterm_id, value => $t->get_year() }; +# } + + +# if (defined $management_factor_date){ +# push @treatment_project_props, { type_id => $self->get_management_factor_date_cvterm_id, value => $management_factor_date }; +# } + +# my @treatment_nd_experiment_project = ( +# { nd_experiment_id => $nd_experiment->nd_experiment_id } +# ); + +# my @treatment_relationships = ( +# { type_id => $self->get_trial_treatment_relationship_cvterm_id, object_project_id => $self->get_trial_id } +# ); + +# #Create a project for each treatment_name +# my $project_treatment_name = $self->get_trial_name()."_".$treatment_name; +# my $treatment_project = $chado_schema->resultset('Project::Project')->create({ +# name => $project_treatment_name, +# description => $management_factor_description, +# projectprops => \@treatment_project_props, +# project_relationship_subject_projects => \@treatment_relationships, +# nd_experiment_projects => \@treatment_nd_experiment_project +# }); + +# if ($self->get_new_treatment_date()) { +# my $management_factor_t = CXGN::Trial->new({ +# bcs_schema => $chado_schema, +# trial_id => $treatment_project->project_id() +# }); +# $management_factor_t->set_management_factor_date($self->get_new_treatment_date() ); +# } +# } +# } print STDERR "Design Stored ".localtime."\n"; }; diff --git a/lib/CXGN/Trial/TrialDesignStore/PhenotypingTrial.pm b/lib/CXGN/Trial/TrialDesignStore/PhenotypingTrial.pm index 1e5588515b..ae747632bb 100644 --- a/lib/CXGN/Trial/TrialDesignStore/PhenotypingTrial.pm +++ b/lib/CXGN/Trial/TrialDesignStore/PhenotypingTrial.pm @@ -34,7 +34,7 @@ sub BUILD { # adjust the cvterm ids for phenotyping trials 'col_number', 'plant_names', 'plot_num_per_block', - 'subplots_names', #For splotplot + 'subplots_names', #For splitplot 'treatments', #For splitplot 'subplots_plant_names', #For splitplot 'additional_info', # For brapi additional info storage diff --git a/lib/CXGN/Trial/TrialLayoutDownload.pm b/lib/CXGN/Trial/TrialLayoutDownload.pm index d22fea0fa9..3c54b324d6 100644 --- a/lib/CXGN/Trial/TrialLayoutDownload.pm +++ b/lib/CXGN/Trial/TrialLayoutDownload.pm @@ -23,7 +23,6 @@ my $trial_layout_download = CXGN::Trial::TrialLayoutDownload->new({ schema => $schema, trial_id => $trial_id, data_level => 'plots', - treatment_project_ids => [1,2], selected_columns => {"plot_name"=>1,"plot_number"=>1,"block_number"=>1}, selected_trait_ids => [1,2,3], }); @@ -60,6 +59,7 @@ use SGN::Model::Cvterm; use CXGN::Stock; use CXGN::Stock::Accession; use JSON; +use List::Util qw(uniq); use CXGN::List::Transform; use CXGN::Phenotypes::Summary; use CXGN::Phenotypes::Exact; @@ -88,11 +88,6 @@ has 'data_level' => ( default => 'plots', ); -has 'treatment_project_ids' => ( - isa => 'ArrayRef[Int]|Undef', - is => 'rw' -); - has 'selected_columns' => ( is => 'ro', isa => 'HashRef', @@ -151,15 +146,6 @@ has 'trial' => ( is => 'rw', ); -#This treatment_info_hash contains all the info needed to make and fill the columns for the various treatments. All of these lists are in the same order. -#A key called treatment_trial_list that is a arrayref of the CXGN::Trial entries that represent the treatments in this trial -#A key called treatment_trial_names_list that is an arrayref of just the treatment names -#A key called treatment_units_hash_list that is a arrayref of hashrefs where the hashrefs indicate the stocks that the treatment was applied to. -has 'treatment_info_hash' => ( - isa => 'HashRef', - is => 'rw', -); - has 'trait_header'=> ( is => 'rw', isa => 'ArrayRef[Str]|Undef', @@ -184,7 +170,6 @@ sub get_layout_output { my $all_stats = $self->all_stats(); my $use_synonyms = $self->use_synonyms(); my %selected_cols = %{$self->selected_columns}; - my $treatments = $self->treatment_project_ids(); my @selected_traits = $self->selected_trait_ids() ? @{$self->selected_trait_ids} : (); my %errors; my @error_messages; @@ -258,18 +243,8 @@ sub get_layout_output { $overall_performance_hash{$_->[0]}->{$_->[8]} = $_; } - my @treatment_trials; - my @treatment_names; - my @treatment_units_array; - if ($treatments){ - foreach (@$treatments){ - my $treatment_trial = CXGN::Trial->new({bcs_schema => $schema, trial_id => $_}); - my $treatment_name = $treatment_trial->get_name(); - push @treatment_trials, $treatment_trial; - push @treatment_names, $treatment_name; - } - } my $exact_performance_hash; + if ($data_level eq 'plots') { if ($include_measured eq 'true') { print STDERR "Getting exact trait values\n"; @@ -278,13 +253,9 @@ sub get_layout_output { trial_id=>$trial_id, data_level=>'plot' }); - $exact_performance_hash = $exact->search(); + $exact_performance_hash = $exact->search(); #this gets treatments too! #print STDERR "Exact Performance hash is ".Dumper($exact_performance_hash)."\n"; } - foreach (@treatment_trials){ - my $treatment_units = $_ ? $_->get_observation_units_direct('plot', ['treatment_experiment']) : []; - push @treatment_units_array, $treatment_units; - } } elsif ($data_level eq 'plants') { if (!$has_plants){ push @error_messages, "Trial does not have plants, so you should not try to download a plant level layout."; @@ -299,10 +270,6 @@ sub get_layout_output { }); $exact_performance_hash = $exact->search(); } - foreach (@treatment_trials){ - my $treatment_units = $_ ? $_->get_observation_units_direct('plant', ['treatment_experiment']) : []; - push @treatment_units_array, $treatment_units; - } } elsif ($data_level eq 'subplots') { if (!$has_subplots){ push @error_messages, "Trial does not have subplots, so you should not try to download a subplot level layout."; @@ -317,10 +284,6 @@ sub get_layout_output { }); $exact_performance_hash = $exact->search(); } - foreach (@treatment_trials){ - my $treatment_units = $_ ? $_->get_observation_units_direct('subplot', ['treatment_experiment']) : []; - push @treatment_units_array, $treatment_units; - } } elsif ($data_level eq 'field_trial_tissue_samples') { if (!$has_tissue_samples){ push @error_messages, "Trial does not have tissue samples, so you should not try to download a tissue sample level layout."; @@ -335,10 +298,6 @@ sub get_layout_output { }); $exact_performance_hash = $exact->search(); } - foreach (@treatment_trials){ - my $treatment_units = $_ ? $_->get_observation_units_direct('tissue_sample', ['treatment_experiment']) : []; - push @treatment_units_array, $treatment_units; - } } elsif ($data_level eq 'plate') { #to make the download in the header for genotyping trials more easily understood, the terms change here if (exists($selected_cols{'plot_name'})){ @@ -352,22 +311,6 @@ sub get_layout_output { $selected_cols{'exported_tissue_sample_name'} = 1; } - print STDERR "Treatment stock hashes\n"; - my @treatment_stock_hashes; - foreach my $u (@treatment_units_array){ - my %treatment_stock_hash; - foreach (@$u){ - $treatment_stock_hash{$_->[1]}++; - } - push @treatment_stock_hashes, \%treatment_stock_hash; - } - - my %treatment_info_hash = ( - treatment_trial_list => \@treatment_trials, - treatment_trial_names_list => \@treatment_names, - treatment_units_hash_list => \@treatment_stock_hashes - ); - #combine sorted exact and overall trait names and if requested convert to synonyms my @exact_trait_names = sort keys %$exact_performance_hash; my @overall_trait_names = sort keys %overall_performance_hash; @@ -397,10 +340,8 @@ sub get_layout_output { trial_id => $trial_id, data_level => $data_level, selected_columns => \%selected_cols, - treatment_project_ids => $treatments, design => $design, trial => $selected_trial, - treatment_info_hash => \%treatment_info_hash, trait_header => \@traits, exact_performance_hash => $exact_performance_hash, overall_performance_hash => \%overall_performance_hash, @@ -432,21 +373,6 @@ sub get_layout_output { return {output => $output}; } -sub _add_treatment_to_line { - my $self = shift; - my $treatment_stock_hashes = shift; - my $line = shift; - my $design_unit_name = shift; - foreach (@$treatment_stock_hashes){ - if(exists($_->{$design_unit_name})){ - push @$line, 1; - } else { - push @$line, ''; - } - } - return $line; -} - sub _add_overall_performance_to_line { my $self = shift; my $overall_trait_names = shift; diff --git a/lib/CXGN/Trial/TrialLayoutDownload/PlantLayout.pm b/lib/CXGN/Trial/TrialLayoutDownload/PlantLayout.pm index 0280330dd7..ce435755ca 100644 --- a/lib/CXGN/Trial/TrialLayoutDownload/PlantLayout.pm +++ b/lib/CXGN/Trial/TrialLayoutDownload/PlantLayout.pm @@ -12,10 +12,8 @@ my $trial_plant_layout = CXGN::Trial::TrialLayoutDownload::PlantLayout->new({ data_level => $data_level, selected_columns => \%selected_cols, selected_trait_ids => \@selected_traits, - treatment_project_ids => $treatments, design => $design, trial => $selected_trial, - treatment_info_hash => \%treatment_info_hash, overall_performance_hash => \%fieldbook_trait_hash, all_stats => $all_stats, }); @@ -47,10 +45,6 @@ sub retrieve { my %selected_cols = %{$self->selected_columns}; my %design = %{$self->design}; my $trial = $self->trial; - my $treatment_info_hash = $self->treatment_info_hash || {}; - my $treatment_list = $treatment_info_hash->{treatment_trial_list} || []; - my $treatment_name_list = $treatment_info_hash->{treatment_trial_names_list} || []; - my $treatment_units_hash_list = $treatment_info_hash->{treatment_units_hash_list} || []; my $trait_header = $self->trait_header || []; my $exact_performance_hash = $self->exact_performance_hash || {}; my $overall_performance_hash = $self->overall_performance_hash || {}; @@ -73,9 +67,6 @@ sub retrieve { } } - foreach (@$treatment_name_list){ - push @header, "Treatment:".$_; - } foreach (@$trait_header){ push @header, $_; } @@ -163,7 +154,6 @@ sub retrieve { } } } - $line = $self->_add_treatment_to_line($treatment_units_hash_list, $line, $design_info->{plant_name}); $line = $self->_add_exact_performance_to_line(\@exact_trait_names, $line, $exact_performance_hash, $design_info->{plant_name}); $line = $self->_add_overall_performance_to_line(\@overall_trait_names, $line, $overall_performance_hash, $design_info, $all_stats); push @output, $line; diff --git a/lib/CXGN/Trial/TrialLayoutDownload/PlotLayout.pm b/lib/CXGN/Trial/TrialLayoutDownload/PlotLayout.pm index 5e0dbef672..a1a9077e70 100644 --- a/lib/CXGN/Trial/TrialLayoutDownload/PlotLayout.pm +++ b/lib/CXGN/Trial/TrialLayoutDownload/PlotLayout.pm @@ -12,10 +12,8 @@ my $trial_plot_layout = CXGN::Trial::TrialLayoutDownload::PlotLayout->new({ data_level => $data_level, selected_columns => \%selected_cols, selected_trait_ids => \@selected_traits, - treatment_project_ids => $treatments, design => $design, trial => $selected_trial, - treatment_info_hash => \%treatment_info_hash, overall_performance_hash => \%fieldbook_trait_hash, all_stats => $all_stats, }); @@ -47,10 +45,6 @@ sub retrieve { my %selected_cols = %{$self->selected_columns}; my %design = %{$self->design}; my $trial = $self->trial; - my $treatment_info_hash = $self->treatment_info_hash || {}; - my $treatment_list = $treatment_info_hash->{treatment_trial_list} || []; - my $treatment_name_list = $treatment_info_hash->{treatment_trial_names_list} || []; - my $treatment_units_hash_list = $treatment_info_hash->{treatment_units_hash_list} || []; my $trait_header = $self->trait_header || []; my $exact_performance_hash = $self->exact_performance_hash || {}; my $overall_performance_hash = $self->overall_performance_hash || {}; @@ -72,9 +66,6 @@ sub retrieve { } } } - foreach (@$treatment_name_list){ - push @header, "Treastment:".$_; - } foreach (@$trait_header){ push @header, $_; } @@ -122,7 +113,6 @@ sub retrieve { #print STDERR "Adding treatment and trait performance\n"; - $line = $self->_add_treatment_to_line($treatment_units_hash_list, $line, $design_info->{'plot_name'}); $line = $self->_add_exact_performance_to_line(\@exact_trait_names, $line, $exact_performance_hash, $design_info->{'plot_name'}); $line = $self->_add_overall_performance_to_line(\@overall_trait_names, $line, $overall_performance_hash, $design_info, $all_stats); push @output, $line; diff --git a/lib/CXGN/Trial/TrialLayoutDownload/SubplotLayout.pm b/lib/CXGN/Trial/TrialLayoutDownload/SubplotLayout.pm index 2d6699633b..df451b00d3 100644 --- a/lib/CXGN/Trial/TrialLayoutDownload/SubplotLayout.pm +++ b/lib/CXGN/Trial/TrialLayoutDownload/SubplotLayout.pm @@ -12,10 +12,8 @@ my $trial_plant_layout = CXGN::Trial::TrialLayoutDownload::SubplotLayout->new({ data_level => $data_level, selected_columns => \%selected_cols, selected_trait_ids => \@selected_traits, - treatment_project_ids => $treatments, design => $design, trial => $selected_trial, - treatment_info_hash => \%treatment_info_hash, overall_performance_hash => \%fieldbook_trait_hash, all_stats => $all_stats, }); @@ -47,10 +45,6 @@ sub retrieve { my %selected_cols = %{$self->selected_columns}; my %design = %{$self->design}; my $trial = $self->trial; - my $treatment_info_hash = $self->treatment_info_hash || {}; - my $treatment_list = $treatment_info_hash->{treatment_trial_list} || []; - my $treatment_name_list = $treatment_info_hash->{treatment_trial_names_list} || []; - my $treatment_units_hash_list = $treatment_info_hash->{treatment_units_hash_list} || []; my $trait_header = $self->trait_header || []; my $exact_performance_hash = $self->exact_performance_hash || {}; my $overall_performance_hash = $self->overall_performance_hash || {}; @@ -73,9 +67,6 @@ sub retrieve { } } - foreach (@$treatment_name_list){ - push @header, "Treatment:".$_; - } foreach (@$trait_header){ push @header, $_; } @@ -143,7 +134,6 @@ sub retrieve { } } } - $line = $self->_add_treatment_to_line($treatment_units_hash_list, $line, $design_info->{subplot_name}); $line = $self->_add_exact_performance_to_line(\@exact_trait_names, $line, $exact_performance_hash, $design_info->{subplot_name}); $line = $self->_add_overall_performance_to_line(\@overall_trait_names, $line, $overall_performance_hash, $design_info, $all_stats); push @output, $line; diff --git a/lib/CXGN/Trial/TrialLayoutDownload/TissueSampleLayout.pm b/lib/CXGN/Trial/TrialLayoutDownload/TissueSampleLayout.pm index 9b5146a2ed..715b53b14f 100644 --- a/lib/CXGN/Trial/TrialLayoutDownload/TissueSampleLayout.pm +++ b/lib/CXGN/Trial/TrialLayoutDownload/TissueSampleLayout.pm @@ -12,10 +12,8 @@ my $trial_tissue_sample_layout = CXGN::Trial::TrialLayoutDownload::TissueSampleL data_level => $data_level, selected_columns => \%selected_cols, selected_trait_ids => \@selected_traits, - treatment_project_ids => $treatments, design => $design, trial => $selected_trial, - treatment_info_hash => \%treatment_info_hash, overall_performance_hash => \%fieldbook_trait_hash, all_stats => $all_stats, }); @@ -47,10 +45,6 @@ sub retrieve { my %selected_cols = %{$self->selected_columns}; my %design = %{$self->design}; my $trial = $self->trial; - my $treatment_info_hash = $self->treatment_info_hash || {}; - my $treatment_list = $treatment_info_hash->{treatment_trial_list} || []; - my $treatment_name_list = $treatment_info_hash->{treatment_trial_names_list} || []; - my $treatment_units_hash_list = $treatment_info_hash->{treatment_units_hash_list} || []; my $trait_header = $self->trait_header || []; my $exact_performance_hash = $self->exact_performance_hash || {}; my $overall_performance_hash = $self->overall_performance_hash || {}; @@ -73,9 +67,6 @@ sub retrieve { } } - foreach (@$treatment_name_list){ - push @header, "Treatment:".$_; - } foreach (@$trait_header){ push @header, $_; } @@ -187,7 +178,6 @@ sub retrieve { } } } - $line = $self->_add_treatment_to_line($treatment_units_hash_list, $line, $design_info->{tissue_sample_name}); $line = $self->_add_exact_performance_to_line(\@exact_trait_names, $line, $exact_performance_hash, $design_info->{tissue_sample_name}); $line = $self->_add_overall_performance_to_line(\@overall_trait_names, $line, $overall_performance_hash, $design_info, $all_stats); push @output, $line; diff --git a/lib/SGN/Controller/AJAX/Cvterm.pm b/lib/SGN/Controller/AJAX/Cvterm.pm index 210a5f5417..3d9000d432 100644 --- a/lib/SGN/Controller/AJAX/Cvterm.pm +++ b/lib/SGN/Controller/AJAX/Cvterm.pm @@ -73,6 +73,37 @@ ORDER BY cv.name, cvterm.name limit 30"; $c->stash->{rest} = \@response_list; } +sub autocompleteslim : Local : ActionClass('REST') { } + +sub autocompleteslim_GET :Args(0) { + my ( $self, $c ) = @_; + + #my $term = $c->req->param('term_name'); + my $db_name = $c->request->param('db_name'); + $db_name = '%'.$db_name.'%'; + # trim and regularize whitespace + #$term =~ s/(^\s+|\s+)$//g; + #$term =~ s/\s+/ /g; + my $term_name = $c->request->param("term"); + + my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado'); + my $query = "SELECT distinct cvterm.cvterm_id as cvterm_id , cv.name as cv_name , cvterm.name as cvterm_name , db.name || ':' || dbxref.accession as accession + FROM db + JOIN dbxref USING (db_id ) JOIN cvterm USING (dbxref_id) + JOIN cv USING (cv_id ) + LEFT JOIN cvtermsynonym USING (cvterm_id ) + WHERE db.name ilike ? AND (cvterm.name ilike ? OR cvtermsynonym.synonym ilike ? OR cvterm.definition ilike ?) AND cvterm.is_obsolete = 0 AND is_relationshiptype = 0 +GROUP BY cvterm.cvterm_id,cv.name, cvterm.name, dbxref.accession, db.name +ORDER BY cv.name, cvterm.name limit 30"; + my $sth= $schema->storage->dbh->prepare($query); + $sth->execute($db_name, "\%$term_name\%", "\%$term_name\%", "\%$term_name\%"); + my @response_list; + while (my ($cvterm_id, $cv_name, $cvterm_name, $accession) = $sth->fetchrow_array() ) { + push @response_list, $cvterm_name . "|" . $accession ; + } + $c->stash->{rest} = \@response_list; +} + sub relationships : Local : ActionClass('REST') { } sub relationships_GET :Args(0) { diff --git a/lib/SGN/Controller/AJAX/LabelDesigner.pm b/lib/SGN/Controller/AJAX/LabelDesigner.pm index fe4c8b55d4..54228d3979 100644 --- a/lib/SGN/Controller/AJAX/LabelDesigner.pm +++ b/lib/SGN/Controller/AJAX/LabelDesigner.pm @@ -390,7 +390,7 @@ __PACKAGE__->config( my $source_name = $c->req->param("source_name"); my $design_json = $c->req->param("design_json"); # decode json - my $json = JSON->new(); + # my $json = JSON->new; #my $design_params = $json->allow_nonref->utf8->relaxed->escape_slash->loose->allow_singlequote->allow_barekey->decode($design_json); my $design_params = decode_json($design_json); my $labels_to_download = $design_params->{'labels_to_download'} || undef; @@ -531,7 +531,7 @@ __PACKAGE__->config( my $label_y = $design_params->{'page_height'} - $design_params->{'top_margin'} - ($design_params->{'label_height'} + $design_params->{'vertical_gap'}) * ($row_num-1); foreach my $element (@$label_params) { - #print STDERR "Element Dumper\n" . Dumper($element); + # print STDERR "Element Dumper\n" . Dumper($element); my %element = %{$element}; my $elementx = $label_x + ( $element{'x'} / $conversion_factor ); my $elementy = $label_y - ( $element{'y'} / $conversion_factor ); @@ -579,18 +579,26 @@ __PACKAGE__->config( } else { #Text - my $font = $pdf->corefont($element{'font'}); # Add a built-in font to the PDF - # Add text to the page - my $adjusted_size = $element{'size'} / $conversion_factor; # scale to 72 pts per inch - $text->font($font, $adjusted_size); - my $height = $element{'height'} / $conversion_factor ; # scale to 72 pts per inch - my $width = $element{'width'} / $conversion_factor; - my $elementy = $elementy - ($height/4); # adjust for img position starting at bottom - $text->translate($elementx, $elementy); - if ($text_alignment eq "middle") { - $text->text_center($filled_value); - } elsif ($text_alignment eq "left") { - $text->text($filled_value); + my $font = $pdf->corefont($element{'font'}); + my $adjusted_size = $element{'size'} / $conversion_factor; + my $height = $element{'height'} / $conversion_factor; + my $line_spacing = $adjusted_size * 1.2; # padding between lines + + my @lines = split(/\n/, $filled_value); # Split multiline text + + # Adjust starting y-position to center the block vertically + my $total_text_height = scalar(@lines) * $line_spacing; + my $start_y = $elementy; + + foreach my $line (@lines) { + $text->font($font, $adjusted_size); + $text->translate($elementx, $start_y); + if ($text_alignment eq "middle") { + $text->text_center($filled_value); + } elsif ($text_alignment eq "left") { + $text->text($filled_value); + } + $start_y -= $line_spacing; } } } @@ -781,14 +789,16 @@ sub get_trial_design { my $trial_name = $schema->resultset("Project::Project")->search({ project_id => $trial_id })->first->name(); my $entry_numbers = $trial->get_entry_numbers(); - my $treatments = $trial->get_treatments(); - my @treatment_ids = map { $_->[0] } @{$treatments}; + my $trial_management_regime = $trial->get_management_regime(); + + # my $treatments = $trial->get_treatments(); + # my @treatment_ids = map { $_->{trait_id} } @{$treatments}; # print STDERR "treatment ids are @treatment_ids\n"; my $trial_layout_download = CXGN::Trial::TrialLayoutDownload->new({ schema => $schema, trial_id => $trial_id, data_level => $type, - treatment_project_ids => \@treatment_ids, + # treatment_ids => \@treatment_ids, selected_columns => $selected_columns{$type}, selected_trait_ids => [], use_synonyms => 'false', @@ -806,21 +816,8 @@ sub get_trial_design { my %detail_hash; @detail_hash{@keys} = @{$outer_array[$i]}; - my @applied_treatments; - foreach my $key (keys %detail_hash) { - if ( $key =~ /ManagementFactor/ && $detail_hash{$key} ) { - my $treatment = $key; - $treatment =~ s/ManagementFactor://; - $treatment =~ s/$trial_name//; - $treatment =~ s/^_//; - push @applied_treatments, $treatment; - delete($detail_hash{$key}); - } - elsif ( $key =~ /ManagementFactor/ ) { - delete($detail_hash{$key}); - } - } - $detail_hash{'management_factor'} = join(",", @applied_treatments); + $detail_hash{'brief_management_regime'} = _format_management_regime($trial_management_regime); + $detail_hash{'full_management_regime'} = $trial_management_regime ? encode_json($trial_management_regime) : ''; $detail_hash{'entry_number'} = $entry_numbers ? $entry_numbers->{$detail_hash{accession_id}} : undef; $mapped_design{$detail_hash{$unique_identifier{$type}}} = \%detail_hash; @@ -851,7 +848,7 @@ sub get_data { my $match = substr($data_level, 6); my $list_data = SGN::Controller::AJAX::List->retrieve_list($c, $id); my @list_data = @{$list_data}; - my $json = JSON->new(); + # my $json = JSON->new; #my $identifier_object = $json->allow_nonref->utf8->relaxed->escape_slash->loose->allow_singlequote->allow_barekey->decode($list_data[0][1]); my $identifier_object = decode_json($list_data[0][1]); my $records = $identifier_object->{'records'}; @@ -1101,6 +1098,25 @@ sub get_additional_list_data { return \%fields; } +sub _format_management_regime { #management regime is a list of hashes. This condenses it because it would be too large otherwise + my $management_regime = shift; + + if (!$management_regime || $management_regime eq "") { + return ""; + } + + my @factors; + + foreach my $factor (@{$management_regime}) { + my $factor_text = ""; + $factor_text .= "Mgmt factor type: ".$factor->{type}."\n"; + $factor_text .= "Description: ".$factor->{description}."\n"; + $factor_text .= "Schedule: ".$factor->{schedule}."\n"; + push @factors, $factor_text; + } + return join("\n", @factors); +} + ######### 1; ######### diff --git a/lib/SGN/Controller/AJAX/Onto.pm b/lib/SGN/Controller/AJAX/Onto.pm index 8fb2f83bab..64b573b625 100644 --- a/lib/SGN/Controller/AJAX/Onto.pm +++ b/lib/SGN/Controller/AJAX/Onto.pm @@ -32,6 +32,8 @@ use CXGN::Chado::Cvterm; use CXGN::Onto; use Data::Dumper; use JSON; +use CXGN::Job; +use Cwd; use namespace::autoclean; @@ -43,6 +45,73 @@ __PACKAGE__->config( map => { 'application/json' => 'JSON' }, ); +=head2 download_obo + +Dumps a DB as a .obo file. Accepts db.name as an argument + +=cut + +sub download_obo: Path('/ajax/onto/download_obo') Args(1) { + my $self = shift; + my $c = shift; + my $db_name = shift; + + my $dbhost = $c->config->{dbhost}; + my $dbname = $c->config->{dbname}; + my $dbuser = $c->config->{dbuser}; + my $dbpass = $c->config->{dbpass}; + + 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 $people_schema = $c->dbic_schema("CXGN::People::Schema", undef, $sp_person_id); + + my $temp_basedir = $c->config->{cluster_shared_tempdir}; + + if (! -d "$temp_basedir/obo_downloads") { + `mkdir $temp_basedir/obo_downloads`; + } + + my $outdir = "$temp_basedir/obo_downloads"; + + my $cmd = "perl ./bin/download_obo.pl -i $db_name -H $dbhost -D $dbname -U $dbuser -P $dbpass -o $outdir"; + + print STDERR "Dumping OBO file at $outdir/$db_name.breedbase.obo\n"; + + my $obo_downloader; + + eval { + + $obo_downloader = CXGN::Job->new({ + people_schema => $people_schema, + schema => $schema, + sp_person_id => $sp_person_id, + cmd => $cmd, + finish_logfile => $c->config->{job_finish_log}, + name => "$db_name ontology download", + job_type => 'download', + submit_page => $c->req->path + }); + + $obo_downloader->submit(); + + }; + + if ($@) { + $c->stash->rest = {error => "An error occurred starting the download: $@"}; + return; + } + + $obo_downloader->wait(); + + $c->res->content_type('application/octet-stream'); + $c->res->header('Content-Disposition' => "attachment; filename=\"$db_name.obo\""); + $c->res->body(do { + open my $f, '<', "$outdir/$db_name.breedbase.obo" or die "Can't open file: $!"; + local $/; #this slurps a file I think + <$f>; + }); +} + =head2 compose_trait Creates a new term in the designated composed trait cv and links it to component terms through cvterm_relationship @@ -58,11 +127,12 @@ sub compose_trait: Path('/ajax/onto/store_composed_term') Args(0) { #print STDERR "Ids array for composing in AJAX Onto = @ids\n"; my $new_trait_names = decode_json $c->req->param("new_trait_names"); + my $term_type = $c->req->param("type") ? $c->req->param("type") : 'trait'; #print STDERR Dumper $new_trait_names; my $new_terms; eval { my $onto = CXGN::Onto->new( { schema => $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado') } ); - $new_terms = $onto->store_composed_term($new_trait_names); + $new_terms = $onto->store_composed_term($new_trait_names, $term_type); }; if ($@) { $c->stash->{rest} = { error => "An error occurred saving the new trait details: $@" }; diff --git a/lib/SGN/Controller/AJAX/PhenotypesUpload.pm b/lib/SGN/Controller/AJAX/PhenotypesUpload.pm index 8ee8f8c584..5a6ce45947 100644 --- a/lib/SGN/Controller/AJAX/PhenotypesUpload.pm +++ b/lib/SGN/Controller/AJAX/PhenotypesUpload.pm @@ -43,12 +43,12 @@ __PACKAGE__->config( sub upload_phenotype_verify : Path('/ajax/phenotype/upload_verify') : ActionClass('REST') { } sub upload_phenotype_verify_POST : Args(1) { - my ($self, $c, $file_type) = @_; + my ($self, $c, $file_type, $is_treatment) = @_; my $schema = $c->dbic_schema("Bio::Chado::Schema"); my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema"); my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema"); - my ($success_status, $error_status, $parsed_data, $plots, $traits, $phenotype_metadata, $timestamp_included, $overwrite_values, $remove_values, $image_zip, $user_id, $validate_type) = _prep_upload($c, $file_type, $schema); + my ($success_status, $error_status, $parsed_data, $plots, $traits, $phenotype_metadata, $timestamp_included, $overwrite_values, $remove_values, $image_zip, $user_id, $validate_type) = _prep_upload($c, $file_type, $is_treatment, $schema); if (scalar(@$error_status)>0) { $c->stash->{rest} = {success => $success_status, error => $error_status }; return; @@ -106,12 +106,12 @@ sub upload_phenotype_verify_POST : Args(1) { sub upload_phenotype_store : Path('/ajax/phenotype/upload_store') : ActionClass('REST') { } sub upload_phenotype_store_POST : Args(1) { - my ($self, $c, $file_type) = @_; + my ($self, $c, $file_type, $is_treatment) = @_; my $schema = $c->dbic_schema("Bio::Chado::Schema"); my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema"); my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema"); - my ($success_status, $error_status, $parsed_data, $plots, $traits, $phenotype_metadata, $timestamp_included, $overwrite_values, $remove_values, $image_zip, $user_id, $validate_type) = _prep_upload($c, $file_type, $schema); + my ($success_status, $error_status, $parsed_data, $plots, $traits, $phenotype_metadata, $timestamp_included, $overwrite_values, $remove_values, $image_zip, $user_id, $validate_type) = _prep_upload($c, $file_type, $is_treatment, $schema); if (scalar(@$error_status)>0) { $c->stash->{rest} = {success => $success_status, error => $error_status }; return; @@ -197,13 +197,14 @@ sub upload_phenotype_store_POST : Args(1) { } sub _prep_upload { - my ($c, $file_type, $schema) = @_; + my ($c, $file_type, $is_treatment, $schema) = @_; my @success_status; my @error_status; my $user = $c->user(); - if (!$user) { - push @error_status, 'Must be logged in to upload phenotypes!'; + + if (!$user) {# only checks for login, ask whether this needs to be changed... + push @error_status, 'You do not have permission to upload data to this trial!'; return (\@success_status, \@error_status); } @@ -217,7 +218,20 @@ sub _prep_upload { my $data_level; my $image_zip; if ($file_type eq "spreadsheet") { - my $spreadsheet_format = $c->req->param('upload_spreadsheet_phenotype_file_format'); #simple or detailed or nirs or scio or associated_images + my $spreadsheet_format; + if ($is_treatment eq "treatment") { + $spreadsheet_format = $c->req->param("upload_spreadsheet_treatment_file_format"); + $timestamp_included = $c->req->param('upload_spreadsheet_treatment_timestamp_checkbox'); + $data_level = $c->req->param('upload_spreadsheet_treatment_data_level') || 'plots'; + $upload = $c->req->upload('upload_spreadsheet_treatment_file_input'); + $image_zip = $c->req->upload('upload_spreadsheet_treatment_associated_images_file_input'); + } else { + $spreadsheet_format = $c->req->param("upload_spreadsheet_phenotype_file_format"); #simple or detailed or nirs or scio or associated_images + $timestamp_included = $c->req->param('upload_spreadsheet_phenotype_timestamp_checkbox'); + $data_level = $c->req->param('upload_spreadsheet_phenotype_data_level') || 'plots'; + $upload = $c->req->upload('upload_spreadsheet_phenotype_file_input'); + $image_zip = $c->req->upload('upload_spreadsheet_phenotype_associated_images_file_input'); + } # print STDERR "File type is Spreadsheet and format is $spreadsheet_format\n"; $metadata_file_type = "spreadsheet phenotype file"; @@ -231,10 +245,6 @@ sub _prep_upload { die "Spreadsheet format not supported! Only simple, detailed, nirs, scio, or associated_images\n"; } $subdirectory = "spreadsheet_phenotype_upload"; - $timestamp_included = $c->req->param('upload_spreadsheet_phenotype_timestamp_checkbox'); - $data_level = $c->req->param('upload_spreadsheet_phenotype_data_level') || 'plots'; - $upload = $c->req->upload('upload_spreadsheet_phenotype_file_input'); - $image_zip = $c->req->upload('upload_spreadsheet_phenotype_associated_images_file_input'); } elsif ($file_type eq "fieldbook") { # print STDERR "Fieldbook \n"; @@ -361,13 +371,30 @@ sub _prep_upload { my %parsed_data; my @plots; my @traits; - if (scalar(@error_status) == 0) { + + if (scalar(@error_status) == 0) { #TODO: check for treatment and propagate values to child stocks if ($parsed_file && !$parsed_file->{'error'}) { %parsed_data = %{$parsed_file->{'data'}}; @plots = @{$parsed_file->{'units'}}; @traits = @{$parsed_file->{'variables'}}; push @success_status, "File data successfully parsed."; } + if ($is_treatment eq "treatment") { + foreach my $plot (@plots) { + my $plot_obj = CXGN::Stock->new({ + schema => $schema, + uniquename => $plot + }); + my $child_stocks = $plot_obj->get_child_stocks_flat_list(); + foreach my $child (@{$child_stocks}) { + next if ($child->{type} eq "accession"); + push @plots, $child->{name}; + foreach my $trait (@traits) { + $parsed_data{$child->{name}}->{$trait} = $parsed_data{$plot}->{$trait}; + } + } + } + } } return (\@success_status, \@error_status, \%parsed_data, \@plots, \@traits, \%phenotype_metadata, $timestamp_included, $overwrite_values, $remove_values, $archived_image_zipfile_with_path, $user_id, $validate_type); diff --git a/lib/SGN/Controller/AJAX/Search/Trait.pm b/lib/SGN/Controller/AJAX/Search/Trait.pm index b5532ef112..2c10f8715a 100644 --- a/lib/SGN/Controller/AJAX/Search/Trait.pm +++ b/lib/SGN/Controller/AJAX/Search/Trait.pm @@ -57,7 +57,7 @@ sub search : Path('/ajax/search/traits') Args(0) { my $trait_search = CXGN::Trait::Search->new({ bcs_schema=>$schema, - is_variable=>1, + is_variable=>1, ontology_db_id_list => $ontology_db_ids, limit => $limit, offset => $offset, diff --git a/lib/SGN/Controller/AJAX/Search/Treatment.pm b/lib/SGN/Controller/AJAX/Search/Treatment.pm new file mode 100644 index 0000000000..f998657fb8 --- /dev/null +++ b/lib/SGN/Controller/AJAX/Search/Treatment.pm @@ -0,0 +1,157 @@ + +package SGN::Controller::AJAX::Search::Treatment; + +use Moose; +use Data::Dumper; +use CXGN::Trait; +use CXGN::Trait::Search; +use CXGN::BreederSearch; + + +BEGIN { extends 'Catalyst::Controller::REST'; } + +__PACKAGE__->config( + default => 'application/json', + stash_key => 'rest', + map => { 'application/json' => 'JSON' }, +); + +sub search : Path('/ajax/search/treatments') Args(0) { + my $self = shift; + my $c = shift; + 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 $params = $c->req->params() || {}; + + my $ontology_db_ids = []; + if ($params->{'ontology_db_id[]'}){ + $ontology_db_ids = ref($params->{'ontology_db_id[]'}) eq 'ARRAY' ? $params->{'ontology_db_id[]'} : [$params->{'ontology_db_id[]'}]; + } + + my $observation_variables = CXGN::BrAPI::v1::ObservationVariables->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $sp_person_id), + phenome_schema=>$c->dbic_schema("CXGN::Phenome::Schema", undef, $sp_person_id), + people_schema => $c->dbic_schema("CXGN::People::Schema", undef, $sp_person_id), + page_size => 1000000, + page => 0, + status => [] + }); + + my $result = $observation_variables->observation_variable_ontologies({cvprop_type_names => ['experiment_treatment_ontology', 'composed_experiment_treatment_ontology']}); + + my @ontos; + if (scalar(@{$ontology_db_ids}) == 0) { + foreach my $o (@{$result->{result}->{data}}) { + push @ontos, $o->{ontologyDbId}; + } + } else { + @ontos = @{$ontology_db_ids}; + } + + my $rows = $params->{length}; + my $offset = $params->{start}; + my $limit = defined($offset) && defined($rows) ? ($offset+$rows)-1 : undef; + + my $treatment_search_list_id = $params->{treatment_search_list_id}; + + my $subset_treatments = []; + if ($treatment_search_list_id){ + my $list = CXGN::List->new({ dbh => $c->dbc->dbh, list_id => $treatment_search_list_id }); + foreach (@{$list->elements()}){ + my @treatment = split '\|', $_; + pop @treatment; + my $treatment_name = join '\|', @treatment; + push @$subset_treatments, $treatment_name; + } + } + + if ($params->{treatment_any_name}){ + push @$subset_treatments, $params->{treatment_any_name}; + } + + my $definitions; + if ($params->{treatment_definition}){ + push @$definitions, $params->{treatment_definition}; + } + + my $trait_search = CXGN::Trait::Search->new({ + bcs_schema=>$schema, + is_variable=>1, + ontology_db_id_list => \@ontos, + limit => $limit, + offset => $offset, + trait_name_list => $subset_treatments, + trait_definition_list => $definitions + }); + my ($data, $records_total) = $trait_search->search(); + my @result; + + my $dbh = $c->dbc->dbh(); + my $bs = CXGN::BreederSearch->new( { dbh=>$dbh } ); + + foreach (@$data){ + my $db_name = $_->{db_name}; + my $accession = $_->{accession}; + my $treatment_id = $_->{trait_id}; + my $treatment_accession = $db_name .":". $accession ; + my $treatment_usage = "None"; + + # Get the number of trials that applied the treatment + my $trial_criteria_list = ['traits', 'trials']; + my $trial_dataref = { + 'trials' => { + 'traits' => $treatment_id + } + }; + my $trial_queryref = { + 'trials' => { + 'traits' => 0 + } + }; + my $trial_results_ref = $bs->metadata_query($trial_criteria_list, $trial_dataref, $trial_queryref); + my $trials = $trial_results_ref->{results}; + my $trial_count = $#{$trials} + 1; + + # Get the number of plots that applied the treatment + if ( $trial_count && $trial_count > 0 ) { + my $plot_criteria_list = ['traits', 'plots']; + my $plot_dataref = { + 'plots' => { + 'traits' => $treatment_id + } + }; + my $plot_queryref = { + 'plots' => { + 'traits' => 0 + } + }; + my $plot_results_ref = $bs->metadata_query($plot_criteria_list, $plot_dataref, $plot_queryref); + my $plots = $plot_results_ref->{results}; + my $plot_count = $#{$plots} + 1; + + $treatment_usage = "Trials: $trial_count
    Plots: $plot_count"; + } + + + push @result, + [ + '', + "", + "{trait_id}/view\">$treatment_accession", + "{trait_id}/view\">$_->{trait_name}", + $_->{trait_definition}, + $treatment_usage, + $_->{trait_name}, + $treatment_accession + ]; + } + #print STDERR Dumper \@result; + + my $draw = $params->{draw}; + if ($draw){ + $draw =~ s/\D//g; # cast to int + } + + $c->stash->{rest} = { data => [ @result ], draw => $draw, recordsTotal => $records_total, recordsFiltered => $records_total }; +} diff --git a/lib/SGN/Controller/AJAX/Stock.pm b/lib/SGN/Controller/AJAX/Stock.pm index 7efea54d0e..d8086e7b70 100644 --- a/lib/SGN/Controller/AJAX/Stock.pm +++ b/lib/SGN/Controller/AJAX/Stock.pm @@ -25,7 +25,6 @@ use Try::Tiny; use CXGN::Phenome::Schema; use CXGN::Phenome::Allele; use CXGN::Stock; -use CXGN::Stock::Plot; use CXGN::Page::FormattingHelpers qw/ columnar_table_html info_table_html html_alternate_show /; use CXGN::Phenome::DumpGenotypes; use CXGN::BreederSearch; @@ -2063,7 +2062,7 @@ sub stock_lookup_POST { $c->stash->{rest} = { $lookup_from_field => $value_to_lookup, $lookup_field => $value }; } -sub get_plot_contents : Path('/stock/get_plot_contents') Args(1) { +sub get_child_stocks : Path('/stock/get_child_stocks') Args(1) { my $self = shift; my $c = shift; my $plot_id = shift; @@ -2073,9 +2072,9 @@ sub get_plot_contents : Path('/stock/get_plot_contents') Args(1) { my $plot_contents; eval { - $plot = CXGN::Stock::Plot->new({schema => $schema, stock_id=>$plot_id}); + $plot = CXGN::Stock->new({schema => $schema, stock_id=>$plot_id}); - $plot_contents = $plot->get_plot_contents(); + $plot_contents = $plot->get_child_stocks(); }; if ($@) { diff --git a/lib/SGN/Controller/AJAX/Trait.pm b/lib/SGN/Controller/AJAX/Trait.pm new file mode 100644 index 0000000000..d6939fd86b --- /dev/null +++ b/lib/SGN/Controller/AJAX/Trait.pm @@ -0,0 +1,248 @@ +package SGN::Controller::AJAX::Trait; + +use Moose; +use CXGN::Trait; + +BEGIN {extends 'Catalyst::Controller::REST'}; + +use strict; +use warnings; + +__PACKAGE__->config( + default => 'application/json', + stash_key => 'rest', + map => { 'application/json' => 'JSON' }, +); + +sub create_trait :Path('/ajax/trait/create') { + my $self = shift; + my $c = shift; + + 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); + + if (!($c->user() && $c->user->check_roles('curator'))) { + $c->stash->{rest} = {error => "You do not have permission to design new traits.\n"}; + return; + } + + if (! $c->config->{allow_trait_edits}) { + $c->stash->{rest} = {error => "You do not have permission to design new traits.\n"}; + return; + } + + my $name = $c->req->param('name') ? $c->req->param('name') : undef; + my $definition = $c->req->param('definition') ? $c->req->param('definition') : undef; + my $format = $c->req->param('format') ? $c->req->param('format') : undef; + my $default_value = $c->req->param('default_value') ? $c->req->param('default_value') : undef; + my $minimum = $c->req->param('minimum') ? $c->req->param('minimum') : undef; + my $maximum = $c->req->param('maximum') ? $c->req->param('maximum') : undef; + my $categories = $c->req->param('categories') ? $c->req->param('categories') : undef; + my $category_details = $c->req->param('category_details') ? $c->req->param('category_details') : undef; + my $repeat_type = $c->req->param('repeat_type') ? $c->req->param('repeat_type') : undef; + my $parent_term = $c->req->param('parent_term') ? $c->req->param('parent_term') : undef; + + $name =~ s/^\s+//; + $name =~ s/\s+$//; + $name =~ s/_/ /g; + $name =~ s/[^\p{Alpha} ]//g; + if ($format ne "ontology") { + $name = lc($name); + } + + $definition =~ s/^\s+//; + $definition =~ s/\s+$//; + + if (defined($categories)) { + $categories = lc($categories); + $categories =~ s/^\s+//; + $categories =~ s/\s+$//; + } + + if (defined($default_value)) { + $default_value = lc($default_value); + $default_value =~ s/^\s+//; + $default_value =~ s/\s+$//; + } + + my $error = ""; + + if (!$name) { + $error .= "You must supply a name.\n"; + } + if (!$definition) { + $error .= "You must supply a definition.\n"; + } + if (defined($definition) && $definition !~ m/([^\s]+\s+){6,}/) { + $error .= "You supplied a definition, but it seems short. Please ensure the definition fully describes the trait and allows it to be differentiated from other traits.\n"; + } + if (!$format || $format !~ m/numeric|categorical|date|percent|counter|boolean|text|ontology/i) { + $error .= "Treatment format must be numeric, categorical, date, percent, counter, boolean, text, or ontology.\n"; + } + if (defined($categories) && defined($default_value) && $categories !~ m/$default_value/) { + $error .= "The default value of the trait is not in the categories list.\n"; + } + if (defined($default_value) && $default_value =~ m/[=\/]/) { + $error .= "The default value you supplied contains special characters.\n"; + } + if (defined($minimum) && defined($maximum) && $maximum < $minimum) { + $error .= "The maximum value cannot be less than the minimum value.\n"; + } + if ($repeat_type && $repeat_type ne 'single' && $repeat_type ne 'multiple' && $repeat_type ne 'time_series') { + $error .- "Invalid repeat type. Must be single, multiple, or time_series.\n"; + } + + if ($error) { + $c->stash->{rest} = {error => $error}; + return; + } + + my $new_trait; + + eval { + if ($format =~ m/numeric|percent|counter|boolean/i) { + $new_trait = CXGN::Trait->new({ + bcs_schema => $schema, + definition => $definition, + name => $name, + format => $format + }); + if (defined($minimum)) { + $new_trait->minimum($minimum); + } + if (defined($maximum)) { + $new_trait->maximum($maximum); + } + if ($repeat_type) { + $new_trait->repeat_type($repeat_type); + } + } elsif ($format eq "categorical") { + $new_trait = CXGN::Trait->new({ + bcs_schema => $schema, + name => $name, + definition => $definition, + format => $format + }); + if (defined($categories)) { + $new_trait->categories($categories); + $new_trait->category_details($category_details); + } + if ($repeat_type) { + $new_trait->repeat_type($repeat_type); + } + } elsif ($format eq "ontology" || $format eq "date" || $format eq "text") { + $new_trait = CXGN::Trait->new({ + bcs_schema => $schema, + name => $name, + definition => $definition, + format => $format + }); + } + + if (defined($default_value)) { + $new_trait->default_value($default_value); + } + + $new_trait->interactive_store($parent_term); + }; + + if ($@) { + $c->stash->{rest} = {error => "An error occurred trying to create trait: $@"}; + return; + } + + $c->stash->{rest} = {success => 1}; +} + +sub edit_trait :Path('/ajax/trait/edit') { + my $self = shift; + my $c = shift; + + 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); + + if (!($c->user() && $c->user->check_roles('curator'))) { + $c->stash->{rest} = {error => "You do not have permission to edit cvterms.\n"}; + return; + } + + if (! $c->config->{allow_trait_edits}) { + $c->stash->{rest} = {error => "You do not have permission to edit cvterms. Check server configuration.\n"}; + return; + } + + my $cvterm_id = $c->req->param('cvterm_id') ? $c->req->param('cvterm_id') : undef; + my $new_name = $c->req->param('new_name') ? $c->req->param('new_name') : undef; + my $new_definition = $c->req->param('new_definition') ? $c->req->param('new_definition') : undef; + + if (!$cvterm_id) { + $c->stash->{rest} = {error => "Cvterm ID missing.\n"}; + return; + } + + eval { + my $trait = CXGN::Trait->new({ + bcs_schema => $schema, + cvterm_id => $cvterm_id + }); + + if (defined($new_name)) { + $trait->name($new_name); + } + if (defined($new_definition)) { + $trait->definition($new_definition); + } + + $trait->interactive_update(); + }; + + if ($@) { + $c->stash->{rest} = {error => "An error occurred updating cvterm: $@"}; + return; + } + + $c->stash->{rest} = {success => 1}; +} + +sub delete_trait :Path('/ajax/trait/delete') { + my $self = shift; + my $c = shift; + + 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); + + if (!($c->user() && $c->user->check_roles('curator'))) { + $c->stash->{rest} = {error => "You do not have permission to edit cvterms.\n"}; + return; + } + + if (! $c->config->{allow_trait_edits}) { + $c->stash->{rest} = {error => "You do not have permission to edit cvterms. Check server configuration.\n"}; + return; + } + + my $cvterm_id = $c->req->param('cvterm_id') ? $c->req->param('cvterm_id') : undef; + + if (!$cvterm_id) { + $c->stash->{rest} = {error => "Cvterm ID missing.\n"}; + return; + } + + eval { + my $trait = CXGN::Trait->new({ + bcs_schema => $schema, + cvterm_id => $cvterm_id + }); + + $trait->delete(); + }; + + if ($@) { + $c->stash->{rest} = {error => "An error occurred deleting cvterm: $@"}; + return; + } + + $c->stash->{rest} = {success => 1}; +} + +1; \ No newline at end of file diff --git a/lib/SGN/Controller/AJAX/Treatment.pm b/lib/SGN/Controller/AJAX/Treatment.pm new file mode 100644 index 0000000000..5babe65a0a --- /dev/null +++ b/lib/SGN/Controller/AJAX/Treatment.pm @@ -0,0 +1,156 @@ +package SGN::Controller::AJAX::Treatment; + +use Moose; +use CXGN::Trait::Treatment; + +BEGIN {extends 'Catalyst::Controller::REST'}; + +use strict; +use warnings; + +__PACKAGE__->config( + default => 'application/json', + stash_key => 'rest', + map => { 'application/json' => 'JSON' }, +); + +sub create_treatment :Path('/ajax/treatment/create') { + my $self = shift; + my $c = shift; + + 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); + + if (!($c->user() && $c->user->check_roles('curator'))) { + $c->stash->{rest} = {error => "You do not have permission to design new treatments.\n"}; + return; + } + + if (! $c->config->{allow_treatment_edits}) { + $c->stash->{rest} = {error => "You do not have permission to design new treatments.\n"}; + return; + } + + my $name = $c->req->param('name') ? $c->req->param('name') : undef; + my $definition = $c->req->param('definition') ? $c->req->param('definition') : undef; + my $format = $c->req->param('format') ? $c->req->param('format') : undef; + my $default_value = $c->req->param('default_value') ? $c->req->param('default_value') : undef; + my $minimum = $c->req->param('minimum') ? $c->req->param('minimum') : undef; + my $maximum = $c->req->param('maximum') ? $c->req->param('maximum') : undef; + my $categories = $c->req->param('categories') ? $c->req->param('categories') : undef; + my $category_details = $c->req->param('category_details') ? $c->req->param('category_details') : undef; + my $repeat_type = $c->req->param('repeat_type') ? $c->req->param('repeat_type') : undef; + my $parent_term = $c->req->param('parent_term') || 'Experimental treatment ontology|EXPERIMENT_TREATMENT:0000000'; + + $name =~ s/^\s+//; + $name =~ s/\s+$//; + $name =~ s/_/ /g; + $name =~ s/[^\p{Alpha} ]//g; + if ($format ne "ontology") { + $name = lc($name); + } + + $definition =~ s/^\s+//; + $definition =~ s/\s+$//; + + if (defined($categories)) { + $categories =~ s/^\s+//; + $categories =~ s/\s+$//; + } + + if (defined($default_value)) { + $default_value = lc($default_value); + $default_value =~ s/^\s+//; + $default_value =~ s/\s+$//; + } + + my $error = ""; + + if (!$name) { + $error .= "You must supply a name.\n"; + } + if (!$definition) { + $error .= "You must supply a definition.\n"; + } + if (defined($definition) && $definition !~ m/([^\s]+\s+){6,}/) { + $error .= "You supplied a definition, but it seems short. Please ensure the definition fully describes the treatment and allows it to be differentiated from other treatments.\n"; + } + if (!$format || $format !~ m/numeric|categorical|date|percent|counter|boolean|text|ontology/i) { + $error .= "Treatment format must be numeric, categorical, date, percent, counter, boolean, text, or ontology.\n"; + } + if (defined($categories) && defined($default_value) && $categories !~ m/$default_value/) { + $error .= "The default value of the treatment is not in the categories list.\n"; + } + if (defined($default_value) && $default_value =~ m/[=\/]/) { + $error .= "The default value you supplied contains special characters.\n"; + } + if (defined($minimum) && defined($maximum) && $maximum < $minimum) { + $error .= "The maximum value cannot be less than the minimum value.\n"; + } + if (defined($repeat_type) && $repeat_type ne 'single' && $repeat_type ne 'multiple' && $repeat_type ne 'time_series') { + $error .- "Invalid repeat type. Must be single, multiple, or time_series.\n"; + } + + if ($error) { + $c->stash->{rest} = {error => $error}; + return; + } + + my $new_treatment; + + eval { + if ($format =~ m/numeric|percent|counter|boolean/i) { + $new_treatment = CXGN::Trait::Treatment->new({ + bcs_schema => $schema, + definition => $definition, + name => $name, + format => $format + }); + if (defined($minimum)) { + $new_treatment->minimum($minimum); + } + if (defined($maximum)) { + $new_treatment->maximum($maximum); + } + if ($repeat_type) { + $new_treatment->repeat_type($repeat_type); + } + } elsif ($format eq "categorical") { + $new_treatment = CXGN::Trait::Treatment->new({ + bcs_schema => $schema, + name => $name, + definition => $definition, + format => $format, + }); + if ($categories ne "") { + $new_treatment->categories($categories); + $new_treatment->category_details($category_details); + } + if ($repeat_type) { + $new_treatment->repeat_type($repeat_type); + } + } elsif ($format eq "ontology" || $format eq "date" || $format eq "text") { + $new_treatment = CXGN::Trait::Treatment->new({ + bcs_schema => $schema, + name => $name, + definition => $definition, + format => $format + }); + } + + if (defined($default_value)) { + $new_treatment->default_value($default_value); + } + + $new_treatment->store($parent_term); + }; + + if ($@) { + $c->stash->{rest} = {error => "An error occurred trying to create treatment: $@"}; + return; + } + + $c->stash->{rest} = {success => 1}; +} + +1; \ No newline at end of file diff --git a/lib/SGN/Controller/AJAX/Trial.pm b/lib/SGN/Controller/AJAX/Trial.pm index f5eeba0d38..b7153154d0 100644 --- a/lib/SGN/Controller/AJAX/Trial.pm +++ b/lib/SGN/Controller/AJAX/Trial.pm @@ -26,6 +26,7 @@ use File::Basename qw | basename dirname|; use File::Copy; use File::Slurp; use File::Spec::Functions; +use File::Temp 'tempfile'; use Digest::MD5; use List::MoreUtils qw /any /; use Data::Dumper; @@ -55,6 +56,8 @@ use CXGN::File::Parse; use CXGN::People::Person; use CXGN::Tools::Run; use CXGN::Job; +use Cwd; +use CXGN::Phenotypes::StorePhenotypes; BEGIN { extends 'Catalyst::Controller::REST' } @@ -116,6 +119,8 @@ sub generate_experimental_design_POST : Args(0) { my $block_col_number=$c->req->param('col_number_per_block'); my $col_number =$c->req->param('col_number'); + my $json = JSON::XS->new(); + my $block_size = $c->req->param('block_size'); my $max_block_size = $c->req->param('max_block_size'); my $plot_prefix = $c->req->param('plot_prefix'); @@ -126,7 +131,10 @@ sub generate_experimental_design_POST : Args(0) { my $fieldmap_col_number = $c->req->param('fieldmap_col_number'); my $fieldmap_row_number = $c->req->param('fieldmap_row_number'); my $plot_layout_format = $c->req->param('plot_layout_format'); - my @treatments = $c->req->param('treatments[]'); + my $treatments = $c->req->param('treatments') ? $c->req->param('treatments') : ""; + if ($treatments) { + $treatments = $json->decode($treatments); + } my $num_plants_per_plot = $c->req->param('num_plants_per_plot'); my $num_seed_per_plot = $c->req->param('num_seed_per_plot'); my $westcott_check_1 = $c->req->param('westcott_check_1'); @@ -160,7 +168,7 @@ sub generate_experimental_design_POST : Args(0) { } if ($design_type eq 'splitplot'){ - if (scalar(@treatments)<1){ + if (scalar(keys(%{$treatments}))<1){ $c->stash->{rest} = { error => "You need to provide at least one treatment for a splitplot design."}; return; } @@ -314,7 +322,7 @@ sub generate_experimental_design_POST : Args(0) { $trial_design->set_submit_host($c->config->{cluster_host}); $trial_design->set_temp_base($c->config->{cluster_shared_tempdir}); $trial_design->set_plot_numbering_scheme($plot_numbering_scheme); - + my $design_created = 0; if ($use_same_layout) { $design_created = 1; @@ -450,8 +458,8 @@ sub generate_experimental_design_POST : Args(0) { $trial_design->set_sub_block_sequence($no_of_sub_block_sequence); } - if (scalar(@treatments)>0) { - $trial_design->set_treatments(\@treatments); + if ($treatments && scalar(keys(%{$treatments}))>0) { + $trial_design->set_treatments($treatments); } if($num_plants_per_plot){ $trial_design->set_num_plants_per_plot($num_plants_per_plot); @@ -548,7 +556,6 @@ sub save_experimental_design_POST : Args(0) { my $error; my $design = _parse_design_from_json($c->req->param('design_json')); - #print STDERR "\nDesign: " . Dumper $design; my @locations; my $multi_location; @@ -707,6 +714,7 @@ sub save_experimental_design_POST : Args(0) { my $trial_id = $save->{'trial_id'}; my $time = DateTime->now(); my $timestamp = $time->ymd(); + my $pheno_timestamp = $time->ymd()."_".$time->hms(); my $calendar_funcs = CXGN::Calendar->new({}); my $formatted_date = $calendar_funcs->check_value_format($timestamp); my $create_date = $calendar_funcs->display_start_date($formatted_date); @@ -723,6 +731,103 @@ sub save_experimental_design_POST : Args(0) { $c->stash->{rest} = {error => "Error saving trial activity info" }; return; } + + if ($c->req->param('design_type') eq "splitplot") { + + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = getcwd(); + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenostore_data_hash = {}; + my %phenostore_stocks = (); + my %phenostore_treatments = (); + + my $treatment_design; + foreach my $design_json (@{$design}) { + my $design = $json->decode($design_json); + $treatment_design = $design->{'treatments'}; + foreach my $unique_treatment (keys(%{$treatment_design->{'treatments'}})) { + my @treatment_pairs = ($unique_treatment =~ m/\{([^{}]+)\}/g); + my $treatments = []; + foreach my $pair (@treatment_pairs) { + my ($treatment, $value) = $pair =~ m/([^=]+)=(.*)/; + $phenostore_treatments{$treatment} = 1; + push @{$treatments}, { + 'treatment' => $treatment, + 'value' => $value + }; + } + my $subplots = $treatment_design->{'treatments'}->{$unique_treatment}; + foreach my $treatment (@{$treatments}) { + foreach my $subplot (@{$subplots}) { + $phenostore_stocks{$subplot} = 1; + my $plants = $treatment_design->{'plants'}->{$subplot}; + $phenostore_data_hash->{$subplot}->{$treatment->{'treatment'}} = [ + $treatment->{'value'}, + $pheno_timestamp, + $user_name, + '', + '' + ]; + foreach my $plant (@{$plants}) { + $phenostore_stocks{$plant} = 1; + $phenostore_data_hash->{$plant}->{$treatment->{'treatment'}} = [ + $treatment->{'value'}, + $pheno_timestamp, + $user_name, + '', + '' + ]; + } + } + } + } + } + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $temp_basedir, + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + bcs_schema => $chado_schema, + metadata_schema => $metadata_schema, + phenome_schema => $phenome_schema, + user_id => $user_id, + stock_list => [keys(%phenostore_stocks)], + trait_list => [keys(%phenostore_treatments)], + values_hash => $phenostore_data_hash, + metadata_hash =>{ + archived_file => 'none', + archived_file_type => 'new trial design with treatments', + operator => $user_name, + date => $pheno_timestamp + } + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + print STDERR "$verified_error\n"; + $c->stash->{rest} = {error => "The trial was saved, but there was an issue applying treatments: $verified_error\n" }; + return; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + print STDERR "$stored_phenotype_error\n"; + $c->stash->{rest} = {error => "The trial was saved, but there was an issue applying treatments: $stored_phenotype_error\n" }; + return; + } + } } my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); @@ -1141,6 +1246,78 @@ sub upload_trial_file_POST : Args(0) { $trial_activity_obj->trial_activities(\%trial_activity); $trial_activity_obj->parent_id($trial_id); my $activity_prop_id = $trial_activity_obj->store(); + + # save treatments if any + if ($parsed_data->{'treatment_design'}) { + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = getcwd(); + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenostore_data_hash = {}; + my %phenostore_stocks = (); + my %phenostore_treatments = (); + + my $time = DateTime->now(); + my $pheno_timestamp = $time->ymd()."_".$time->hms(); + + my $treatment_design = $parsed_data->{'treatment_design'}; + foreach my $plot (keys(%{$treatment_design})) { + $phenostore_stocks{$plot} = 1; + foreach my $treatment (keys(%{$treatment_design->{$plot}})) { + $phenostore_treatments{$treatment} = 1; + $phenostore_data_hash->{$plot}->{$treatment} = [ + $treatment_design->{$plot}->{$treatment}, + $pheno_timestamp, + $user_name, + '','' + ]; + } + } + + my $store_phenotypes = CXGN::Phenotypes::StorePhenotypes->new({ + basepath => $temp_basedir, + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + bcs_schema => $chado_schema, + metadata_schema => $metadata_schema, + phenome_schema => $phenome_schema, + user_id => $user_id, + stock_list => [keys(%phenostore_stocks)], + trait_list => [keys(%phenostore_treatments)], + values_hash => $phenostore_data_hash, + metadata_hash =>{ + archived_file => 'none', + archived_file_type => 'new trial upload with treatments', + operator => $user_name, + date => $pheno_timestamp + } + }); + + my ($verified_warning, $verified_error) = $store_phenotypes->verify(); + + if ($verified_warning) { + warn $verified_warning; + } + if ($verified_error) { + print STDERR "$verified_error\n"; + $c->stash->{rest} = {error => "The trial was saved, but there was an issue applying treatments: $verified_error\n" }; + return; + } + + my ($stored_phenotype_error, $stored_phenotype_success) = $store_phenotypes->store(); + + if ($stored_phenotype_error) { + print STDERR "$stored_phenotype_error\n"; + $c->stash->{rest} = {error => "The trial was saved, but there was an issue applying treatments: $stored_phenotype_error\n" }; + return; + } + } } #print STDERR "Check 5: ".localtime()."\n"; diff --git a/lib/SGN/Controller/AJAX/TrialMetadata.pm b/lib/SGN/Controller/AJAX/TrialMetadata.pm index 8e43ad2cd0..8117f014e2 100644 --- a/lib/SGN/Controller/AJAX/TrialMetadata.pm +++ b/lib/SGN/Controller/AJAX/TrialMetadata.pm @@ -14,7 +14,6 @@ use CXGN::Trial::FieldMap; use JSON; use CXGN::Phenotypes::PhenotypeMatrix; use CXGN::Cross; - use CXGN::Phenotypes::TrialPhenotype; use CXGN::Login; use CXGN::UploadFile; @@ -366,6 +365,7 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { my $start_date = $c->req->param('start_date'); my $end_date = $c->req->param('end_date'); my $include_dateless_items = $c->req->param('include_dateless_items'); + my $split_by_treatments = $c->req->param('split_by_treatments'); my $select_clause_additional = ''; my $group_by_additional = ''; my $order_by_additional = ''; @@ -404,7 +404,7 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { $select_clause_additional = ', accession.uniquename, accession.stock_id'; $group_by_additional = ', accession.stock_id, accession.uniquename'; $stocks_per_accession = $c->stash->{trial}->get_plots_per_accession(); - $order_by_additional = ' ,accession.uniquename DESC'; + $order_by_additional = ' , accession.uniquename DESC'; } if ($display eq 'plants_accession') { $stock_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant', 'stock_type')->cvterm_id(); @@ -412,7 +412,7 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { $select_clause_additional = ', accession.uniquename, accession.stock_id'; $group_by_additional = ', accession.stock_id, accession.uniquename'; $stocks_per_accession = $c->stash->{trial}->get_plants_per_accession(); - $order_by_additional = ' ,accession.uniquename DESC'; + $order_by_additional = ' , accession.uniquename DESC'; } if ($display eq 'tissue_samples_accession') { $stock_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'tissue_sample', 'stock_type')->cvterm_id(); @@ -420,13 +420,42 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { $select_clause_additional = ', accession.uniquename, accession.stock_id'; $group_by_additional = ', accession.stock_id, accession.uniquename'; $stocks_per_accession = $c->stash->{trial}->get_plants_per_accession(); - $order_by_additional = ' ,accession.uniquename DESC'; + $order_by_additional = ' , accession.uniquename DESC'; } - if ($display eq 'analysis_instance') { $stock_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'analysis_instance', 'stock_type')->cvterm_id(); $rel_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'analysis_of', 'stock_relationship')->cvterm_id(); } + my $treatment_group_by = ''; + my $treatment_select = ''; + my $treatment_with = ''; + my $treatment_join = ''; + if ($split_by_treatments) { + $treatment_with = "WITH treatment_by_stock AS ( + SELECT + nes.stock_id AS stock_id, + (((cv2.name::text || '|'::text) + || db2.name::text) + || ':'::text + || dbx2.accession::text + || '='::text + || ph2.value::text) + AS treatment, + cv2.cvterm_id AS treatment_id + FROM phenotype ph2 + JOIN nd_experiment_phenotype USING(phenotype_id) + JOIN nd_experiment_stock nes USING(nd_experiment_id) + JOIN cvterm cv2 ON ph2.cvalue_id = cv2.cvterm_id + JOIN dbxref dbx2 ON cv2.dbxref_id = dbx2.dbxref_id + JOIN db db2 ON dbx2.db_id = db2.db_id + WHERE db2.name LIKE '\%TREATMENT\%' + GROUP BY treatment, treatment_id, stock_id + ) "; + $treatment_join = "JOIN treatment_by_stock + ON treatment_by_stock.stock_id = plot.stock_id "; + $treatment_group_by = ", treatment_by_stock.treatment, treatment_by_stock.treatment_id "; + $treatment_select = ", treatment_by_stock.treatment, treatment_by_stock.treatment_id"; + } my $accession_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'accession', 'stock_type')->cvterm_id(); my $family_name_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'family_name', 'stock_type')->cvterm_id(); @@ -473,33 +502,39 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { # print STDERR "date params : $date_params\n"; - my $q1 = "SELECT (((cvterm.name::text || '|'::text) || db.name::text) || ':'::text) || dbxref.accession::text AS trait, + my $q1 = "$treatment_with + SELECT + (((cvterm.name::text || '|'::text) || db.name::text) || ':'::text) || dbxref.accession::text AS trait, cvterm.cvterm_id, - count(phenotype.value), - to_char(avg(phenotype.value::real), 'FM999990.990'), - to_char(max(phenotype.value::real), 'FM999990.990'), - to_char(min(phenotype.value::real), 'FM999990.990'), - to_char(stddev(phenotype.value::real), 'FM999990.990') + count(phenotype.value) AS n_obs, + to_char(avg(phenotype.value::real), 'FM999990.990') AS avg_val, + to_char(max(phenotype.value::real), 'FM999990.990') AS max_val, + to_char(min(phenotype.value::real), 'FM999990.990') AS min_val, + to_char(stddev(phenotype.value::real), 'FM999990.990') AS sd_val $select_clause_additional - FROM cvterm - JOIN phenotype ON (cvterm_id=cvalue_id) - JOIN nd_experiment_phenotype USING(phenotype_id) - JOIN nd_experiment_project USING(nd_experiment_id) - JOIN nd_experiment_stock USING(nd_experiment_id) - JOIN stock as plot USING(stock_id) - JOIN stock_relationship on (plot.stock_id = stock_relationship.subject_id) - JOIN stock as accession on (accession.stock_id = stock_relationship.object_id) - JOIN dbxref ON cvterm.dbxref_id = dbxref.dbxref_id JOIN db ON dbxref.db_id = db.db_id - WHERE project_id=? - AND phenotype.value~? - AND stock_relationship.type_id=? - AND plot.type_id=? - AND accession.type_id=? - $date_params - GROUP BY (((cvterm.name::text || '|'::text) || db.name::text) || ':'::text) || dbxref.accession::text, cvterm.cvterm_id $group_by_additional + $treatment_select + FROM phenotype + JOIN nd_experiment_phenotype USING(phenotype_id) + JOIN nd_experiment_project USING(nd_experiment_id) + JOIN nd_experiment_stock USING(nd_experiment_id) + JOIN cvterm ON phenotype.cvalue_id = cvterm.cvterm_id + JOIN dbxref ON cvterm.dbxref_id = dbxref.dbxref_id + JOIN db ON dbxref.db_id = db.db_id + JOIN stock AS plot USING (stock_id) + JOIN stock_relationship ON plot.stock_id = stock_relationship.subject_id + JOIN stock AS accession ON accession.stock_id = stock_relationship.object_id + $treatment_join + WHERE project_id = ? + AND phenotype.value ~ ? + AND stock_relationship.type_id = ? + AND plot.type_id = ? + AND accession.type_id = ? + AND db.name NOT LIKE '\%TREATMENT\%' + $date_params + GROUP BY trait, cvterm.cvterm_id $group_by_additional $treatment_group_by ORDER BY cvterm.name ASC - $order_by_additional "; - + $order_by_additional "; + my $h1 = $dbh->prepare($q1); my $numeric_regex = '^-?[0-9]+([,.][0-9]+)?$'; @@ -509,11 +544,13 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { $h1->execute($c->stash->{trial_id}, $numeric_regex, $rel_type_id, $stock_type_id, $trial_stock_type_id, @date_placeholders); my @phenotype_data; - my @numeric_trait_ids; + my %numeric_trait_ids; - while (my ($trait, $trait_id, $count, $average, $max, $min, $stddev, $stock_name, $stock_id) = $h1->fetchrow_array()) { + while (my ($trait, $trait_id, $count, $average, $max, $min, $stddev, $stock_name, $stock_id, $treatment, $treatment_id) = $h1->fetchrow_array()) { + + next if ($trait =~ m/_TREATMENT/); - push @numeric_trait_ids, $trait_id; + $numeric_trait_ids{$trait_id} = 1; my $cv = 0; if ($stddev && $average != 0) { @@ -526,10 +563,18 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { if ($stddev) { $stddev = $round->round($stddev); } my @return_array; - if ($stock_name && $stock_id) { + if ($select_clause_additional && $stock_name && $stock_id) { $total_complete_number = scalar (@{$stocks_per_accession->{$stock_id}}); push @return_array, qq{$stock_name}; } + if ($split_by_treatments) { + if ($select_clause_additional) { + push @return_array, qq{$treatment}; + } + else { + push @return_array, qq{$stock_name}; + } + } my $percent_missing = ''; if ($total_complete_number > $count){ $percent_missing = sprintf("%.2f", 100 -(($count/$total_complete_number)*100))."%"; @@ -539,6 +584,7 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { push @return_array, ( qq{$trait}, $average, $min, $max, $stddev, $cv, $count, $percent_missing, qq{} ); push @phenotype_data, \@return_array; + } # get data from the non-numeric trait ids @@ -547,31 +593,38 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { # prevent sql statement from failing if there are no numeric traits # my $exclude_numeric_trait_ids = ""; - if (@numeric_trait_ids) { - $exclude_numeric_trait_ids = " AND cvterm.cvterm_id NOT IN (".join(",", @numeric_trait_ids).")"; + if (%numeric_trait_ids) { + $exclude_numeric_trait_ids = " AND cvterm.cvterm_id NOT IN (".join(",", keys(%numeric_trait_ids)).")"; } # print STDERR "run the non-numeric query\n"; - my $q = "SELECT (((cvterm.name::text || '|'::text) || db.name::text) || ':'::text) || dbxref.accession::text AS trait, + + my $q = "$treatment_with + SELECT + (((cvterm.name::text || '|'::text) || db.name::text) || ':'::text) || dbxref.accession::text AS trait, cvterm.cvterm_id, - count(phenotype.value) - $select_clause_additional - FROM cvterm - JOIN phenotype ON (cvterm_id=cvalue_id) - JOIN nd_experiment_phenotype USING(phenotype_id) - JOIN nd_experiment_project USING(nd_experiment_id) - JOIN nd_experiment_stock USING(nd_experiment_id) - JOIN stock as plot USING(stock_id) - JOIN stock_relationship on (plot.stock_id = stock_relationship.subject_id) - JOIN stock as accession on (accession.stock_id = stock_relationship.object_id) - JOIN dbxref ON cvterm.dbxref_id = dbxref.dbxref_id JOIN db ON dbxref.db_id = db.db_id - WHERE project_id=? - AND stock_relationship.type_id=? - AND plot.type_id=? - AND accession.type_id=? - $date_params - $exclude_numeric_trait_ids - GROUP BY (((cvterm.name::text || '|'::text) || db.name::text) || ':'::text) || dbxref.accession::text, cvterm.cvterm_id $group_by_additional + count(phenotype.value) AS n_obsx + $select_clause_additional + $treatment_select + FROM phenotype + JOIN nd_experiment_phenotype USING(phenotype_id) + JOIN nd_experiment_project USING(nd_experiment_id) + JOIN nd_experiment_stock USING(nd_experiment_id) + JOIN cvterm ON phenotype.cvalue_id = cvterm.cvterm_id + JOIN dbxref ON cvterm.dbxref_id = dbxref.dbxref_id + JOIN db ON dbxref.db_id = db.db_id + JOIN stock AS plot USING (stock_id) + JOIN stock_relationship ON plot.stock_id = stock_relationship.subject_id + JOIN stock AS accession ON accession.stock_id = stock_relationship.object_id + $treatment_join + WHERE project_id = ? + AND stock_relationship.type_id = ? + AND plot.type_id = ? + AND accession.type_id = ? + AND db.name NOT LIKE '\%TREATMENT\%' + $date_params + $exclude_numeric_trait_ids + GROUP BY trait, cvterm.cvterm_id $group_by_additional $treatment_group_by ORDER BY cvterm.name ASC $order_by_additional "; @@ -581,9 +634,22 @@ sub phenotype_summary : Chained('trial') PathPart('phenotypes') Args(0) { $h->execute($c->stash->{trial_id}, $rel_type_id, $stock_type_id, $trial_stock_type_id, @date_placeholders); - while (my ($trait, $trait_id, $count, $stock_name, $stock_id) = $h->fetchrow_array()) { - my @return_array; - push @return_array, ( qq{$trait}, "NA", "NA", "NA", "NA", "NA", $count, "NA", qq{} ); + while (my ($trait, $trait_id, $count, $stock_name, $stock_id, $treatment, $treatment_id) = $h->fetchrow_array()) { + next if ($trait =~ m/_TREATMENT/); + my @return_array; + if ($select_clause_additional && $stock_name && $stock_id) { + $total_complete_number = scalar (@{$stocks_per_accession->{$stock_id}}); + push @return_array, qq{$stock_name}; + } + if ($split_by_treatments) { + if ($select_clause_additional) { + push @return_array, qq{$treatment}; + } + else { + push @return_array, qq{$stock_name}; + } + } + push @return_array, ( qq{$trait}, "NA", "NA", "NA", "NA", "NA", $count, "NA", qq{} ); push @phenotype_data, \@return_array; } @@ -821,7 +887,7 @@ sub trial_upload_plants : Chained('trial') PathPart('upload_plants') Args(0) { my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_plants_file'); - my $inherits_plot_treatments = $c->req->param('upload_plants_per_plot_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_plants_per_plot_inherit_treatments'); my $plants_per_plot = $c->req->param('upload_plants_per_plot_number'); my $subdirectory = "trial_plants_upload"; @@ -870,36 +936,58 @@ sub trial_upload_plants : Chained('trial') PathPart('upload_plants') Args(0) { $c->detach(); } - my $upload_plants_txn = sub { - my %plot_plant_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $plot_plant_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; - if ($_->{row_num} && $_->{col_num}) { - push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; - } - push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_names}}, $_->{plant_name}; + my %plot_plant_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $plot_plant_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; + if ($_->{row_num} && $_->{col_num}) { + push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_plant_entries(\%plot_plant_hash, $plants_per_plot, $inherits_plot_treatments, $user_id); + push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_names}}, $_->{plant_name}; + } - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_plants_txn); + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); + + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial plants. ($@).\n"; - $c->detach(); + + if ($t->save_plant_entries(\%plot_plant_hash, $plants_per_plot, $inherits_plot_treatments, $user_id, $phenotype_store_config)) { + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => 1 }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_upload_plants_subplot : Chained('trial') PathPart('upload_plants_subplot') Args(0) { @@ -933,7 +1021,7 @@ sub trial_upload_plants_subplot : Chained('trial') PathPart('upload_plants_subpl my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_plants_subplot_file'); - my $inherits_plot_treatments = $c->req->param('upload_plants_per_subplot_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_plants_per_subplot_inherit_treatments'); my $plants_per_subplot = $c->req->param('upload_plants_per_subplot_number'); my $subdirectory = "trial_plants_upload"; @@ -982,36 +1070,57 @@ sub trial_upload_plants_subplot : Chained('trial') PathPart('upload_plants_subpl $c->detach(); } - my $upload_plants_txn = sub { - my %subplot_plant_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $subplot_plant_hash{$_->{subplot_stock_id}}->{subplot_name} = $_->{subplot_name}; - if ($_->{row_num} && $_->{col_num}) { - push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; - } - push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_names}}, $_->{plant_name}; + my %subplot_plant_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $subplot_plant_hash{$_->{subplot_stock_id}}->{subplot_name} = $_->{subplot_name}; + if ($_->{row_num} && $_->{col_num}) { + push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_plant_subplot_entries(\%subplot_plant_hash, $plants_per_subplot, $inherits_plot_treatments, $user_id); + push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_names}}, $_->{plant_name}; + } + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_plants_txn); + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial plants. ($@).\n"; - $c->detach(); + + if ($t->save_plant_subplot_entries(\%subplot_plant_hash, $plants_per_subplot, $inherits_plot_treatments, $user_id, $user_name, $phenotype_store_config)) { + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => "An error occurred uploading plants to subplots." }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_upload_subplots : Chained('trial') PathPart('upload_subplots') Args(0) { @@ -1045,7 +1154,7 @@ sub trial_upload_subplots : Chained('trial') PathPart('upload_subplots') Args(0) my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_subplots_file'); - my $inherits_plot_treatments = $c->req->param('upload_subplots_per_plot_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_subplots_per_plot_inherit_treatments'); my $subplots_per_plot = $c->req->param('upload_subplots_per_plot_number'); my $subdirectory = "trial_subplots_upload"; @@ -1094,33 +1203,55 @@ sub trial_upload_subplots : Chained('trial') PathPart('upload_subplots') Args(0) $c->detach(); } - my $upload_subplots_txn = sub { - my %plot_subplot_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $plot_subplot_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; - push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_names}}, $_->{subplot_name}; - } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_subplot_entries(\%plot_subplot_hash, $subplots_per_plot, $inherits_plot_treatments, $user_id); + my %plot_subplot_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $plot_subplot_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; + push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_names}}, $_->{subplot_name}; + } - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_subplots_txn); + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial subplots. ($@).\n"; - $c->detach(); + + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); + + if ($t->save_subplot_entries(\%plot_subplot_hash, $subplots_per_plot, $inherits_plot_treatments, $user_id, $user_name, $phenotype_store_config)) { + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => "An error occurred uploading subplots." }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_upload_plants_with_index_number : Chained('trial') PathPart('upload_plants_with_plant_index_number') Args(0) { @@ -1154,7 +1285,7 @@ sub trial_upload_plants_with_index_number : Chained('trial') PathPart('upload_pl my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_plants_with_index_number_file'); - my $inherits_plot_treatments = $c->req->param('upload_plants_with_index_number_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_plants_with_index_number_inherit_treatments'); my $plants_per_plot = $c->req->param('upload_plants_with_index_number_per_plot_number'); my $subdirectory = "trial_plants_upload"; @@ -1203,37 +1334,59 @@ sub trial_upload_plants_with_index_number : Chained('trial') PathPart('upload_pl $c->detach(); } - my $upload_plants_txn = sub { - my %plot_plant_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $plot_plant_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; - push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_names}}, $_->{plant_name}; - push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_index_numbers}}, $_->{plant_index_number}; - if ($_->{row_num} && $_->{col_num}) { - push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; - } + my %plot_plant_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $plot_plant_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; + push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_names}}, $_->{plant_name}; + push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_index_numbers}}, $_->{plant_index_number}; + if ($_->{row_num} && $_->{col_num}) { + push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_plant_entries(\%plot_plant_hash, $plants_per_plot, $inherits_plot_treatments, $user_id); + } - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_plants_txn); + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); + + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial plants. ($@).\n"; - $c->detach(); + + if ($t->save_plant_entries(\%plot_plant_hash, $plants_per_plot, $inherits_plot_treatments, $user_id, $phenotype_store_config)) { + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => "An error occurred uploading plants." }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_upload_plants_subplot_with_index_number : Chained('trial') PathPart('upload_plants_subplot_with_plant_index_number') Args(0) { @@ -1267,7 +1420,7 @@ sub trial_upload_plants_subplot_with_index_number : Chained('trial') PathPart('u my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_plants_subplot_with_index_number_file'); - my $inherits_plot_treatments = $c->req->param('upload_plants_subplot_with_index_number_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_plants_subplot_with_index_number_inherit_treatments'); my $plants_per_subplot = $c->req->param('upload_plants_subplot_with_index_number_per_subplot_number'); my $subdirectory = "trial_plants_upload"; @@ -1316,37 +1469,59 @@ sub trial_upload_plants_subplot_with_index_number : Chained('trial') PathPart('u $c->detach(); } - my $upload_plants_txn = sub { - my %subplot_plant_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $subplot_plant_hash{$_->{subplot_stock_id}}->{subplot_name} = $_->{subplot_name}; - push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_names}}, $_->{plant_name}; - push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_index_numbers}}, $_->{plant_index_number}; - if ($_->{row_num} && $_->{col_num}) { - push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; - } + my %subplot_plant_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $subplot_plant_hash{$_->{subplot_stock_id}}->{subplot_name} = $_->{subplot_name}; + push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_names}}, $_->{plant_name}; + push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_index_numbers}}, $_->{plant_index_number}; + if ($_->{row_num} && $_->{col_num}) { + push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_plant_subplot_entries(\%subplot_plant_hash, $plants_per_subplot, $inherits_plot_treatments, $user_id); + } - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_plants_txn); + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); + + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial plants. ($@).\n"; - $c->detach(); + + if ($t->save_plant_subplot_entries(\%subplot_plant_hash, $plants_per_subplot, $inherits_plot_treatments, $user_id, $user_name, $phenotype_store_config)) { + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => "An error occurred uploading plants to subplots." }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_upload_subplots_with_index_number : Chained('trial') PathPart('upload_subplots_with_subplot_index_number') Args(0) { @@ -1380,7 +1555,7 @@ sub trial_upload_subplots_with_index_number : Chained('trial') PathPart('upload_ my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_subplots_with_index_number_file'); - my $inherits_plot_treatments = $c->req->param('upload_subplots_with_index_number_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_subplots_with_index_number_inherit_treatments'); my $subplots_per_plot = $c->req->param('upload_subplots_with_index_number_per_plot_number'); my $subdirectory = "trial_subplots_upload"; @@ -1429,34 +1604,56 @@ sub trial_upload_subplots_with_index_number : Chained('trial') PathPart('upload_ $c->detach(); } - my $upload_subplots_txn = sub { - my %plot_subplot_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $plot_subplot_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; - push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_names}}, $_->{subplot_name}; - push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_index_numbers}}, $_->{subplot_index_number}; - } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_subplot_entries(\%plot_subplot_hash, $subplots_per_plot, $inherits_plot_treatments, $user_id); - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_subplots_txn); + my %plot_subplot_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $plot_subplot_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; + push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_names}}, $_->{subplot_name}; + push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_index_numbers}}, $_->{subplot_index_number}; + } + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); + + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial subplots. ($@).\n"; - $c->detach(); + + if ($t->save_subplot_entries(\%plot_subplot_hash, $subplots_per_plot, $inherits_plot_treatments, $user_id, $user_name, $phenotype_store_config)) { + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => "An error occurred uploading subplots." }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_upload_plants_with_number_of_plants : Chained('trial') PathPart('upload_plants_with_number_of_plants') Args(0) { @@ -1490,7 +1687,7 @@ sub trial_upload_plants_with_number_of_plants : Chained('trial') PathPart('uploa my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_plants_with_number_of_plants_file'); - my $inherits_plot_treatments = $c->req->param('upload_plants_with_num_plants_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_plants_with_num_plants_inherit_treatments'); my $plants_per_plot = $c->req->param('upload_plants_with_num_plants_per_plot_number'); my $subdirectory = "trial_plants_upload"; @@ -1539,37 +1736,58 @@ sub trial_upload_plants_with_number_of_plants : Chained('trial') PathPart('uploa $c->detach(); } - my $upload_plants_txn = sub { - my %plot_plant_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $plot_plant_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; - push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_names}}, $_->{plant_name}; - push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_index_numbers}}, $_->{plant_index_number}; - if ($_->{row_num} && $_->{col_num}) { - push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; - } + my %plot_plant_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $plot_plant_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; + push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_names}}, $_->{plant_name}; + push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_index_numbers}}, $_->{plant_index_number}; + if ($_->{row_num} && $_->{col_num}) { + push @{$plot_plant_hash{$_->{plot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_plant_entries(\%plot_plant_hash, $plants_per_plot, $inherits_plot_treatments, $user_id); + } + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_plants_txn); + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial plants. ($@).\n"; - $c->detach(); + + if ($t->save_plant_entries(\%plot_plant_hash, $plants_per_plot, $inherits_plot_treatments, $user_id, $phenotype_store_config)){ + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => "An error occurred uploading plants." }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_upload_plants_subplot_with_number_of_plants : Chained('trial') PathPart('upload_plants_subplot_with_number_of_plants') Args(0) { @@ -1603,7 +1821,7 @@ sub trial_upload_plants_subplot_with_number_of_plants : Chained('trial') PathPar my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_plants_subplot_with_number_of_plants_file'); - my $inherits_plot_treatments = $c->req->param('upload_plants_subplot_with_num_plants_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_plants_subplot_with_num_plants_inherit_treatments'); my $plants_per_subplot = $c->req->param('upload_plants_subplot_with_num_plants_per_subplot_number'); my $subdirectory = "trial_plants_upload"; @@ -1652,37 +1870,58 @@ sub trial_upload_plants_subplot_with_number_of_plants : Chained('trial') PathPar $c->detach(); } - my $upload_plants_txn = sub { - my %subplot_plant_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $subplot_plant_hash{$_->{subplot_stock_id}}->{subplot_name} = $_->{subplot_name}; - push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_names}}, $_->{plant_name}; - push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_index_numbers}}, $_->{plant_index_number}; - if ($_->{row_num} && $_->{col_num}) { - push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; - } + my %subplot_plant_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $subplot_plant_hash{$_->{subplot_stock_id}}->{subplot_name} = $_->{subplot_name}; + push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_names}}, $_->{plant_name}; + push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_index_numbers}}, $_->{plant_index_number}; + if ($_->{row_num} && $_->{col_num}) { + push @{$subplot_plant_hash{$_->{subplot_stock_id}}->{plant_coords}}, $_->{row_num}.",".$_->{col_num}; } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_plant_subplot_entries(\%subplot_plant_hash, $plants_per_subplot, $inherits_plot_treatments, $user_id); + } + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_plants_txn); + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial plants. ($@).\n"; - $c->detach(); + + if ($t->save_plant_subplot_entries(\%subplot_plant_hash, $plants_per_subplot, $inherits_plot_treatments, $user_id, $user_name, $phenotype_store_config)) { + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => "An error occurred uploading plants to subplots." }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_upload_subplots_with_number_of_subplots : Chained('trial') PathPart('upload_subplots_with_number_of_subplots') Args(0) { @@ -1716,7 +1955,7 @@ sub trial_upload_subplots_with_number_of_subplots : Chained('trial') PathPart('u my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $user_id); my $upload = $c->req->upload('trial_upload_subplots_with_number_of_subplots_file'); - my $inherits_plot_treatments = $c->req->param('upload_subplots_with_num_subplots_inherit_treatments'); + my $inherits_plot_treatments = 1; #$c->req->param('upload_subplots_with_num_subplots_inherit_treatments'); my $subplots_per_plot = $c->req->param('upload_subplots_with_num_subplots_per_plot_number'); my $subdirectory = "trial_subplots_upload"; @@ -1765,34 +2004,55 @@ sub trial_upload_subplots_with_number_of_subplots : Chained('trial') PathPart('u $c->detach(); } - my $upload_subplots_txn = sub { - my %plot_subplot_hash; - my $parsed_entries = $parsed_data->{data}; - foreach (@$parsed_entries){ - $plot_subplot_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; - push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_names}}, $_->{subplot_name}; - push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_index_numbers}}, $_->{subplot_index_number}; - } - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); - $t->save_subplot_entries(\%plot_subplot_hash, $subplots_per_plot, $inherits_plot_treatments, $user_id); + my %plot_subplot_hash; + my $parsed_entries = $parsed_data->{data}; + foreach (@$parsed_entries){ + $plot_subplot_hash{$_->{plot_stock_id}}->{plot_name} = $_->{plot_name}; + push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_names}}, $_->{subplot_name}; + push @{$plot_subplot_hash{$_->{plot_stock_id}}->{subplot_index_numbers}}, $_->{subplot_index_number}; + } + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); - my $layout = $c->stash->{trial_layout}; - $layout->generate_and_cache_layout(); - }; - eval { - $schema->txn_do($upload_subplots_txn); + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } }; - if ($@) { - $c->stash->{rest} = { error => $@ }; - print STDERR "An error condition occurred, was not able to upload trial subplots. ($@).\n"; - $c->detach(); + + if ($t->save_subplot_entries(\%plot_subplot_hash, $subplots_per_plot, $inherits_plot_treatments, $user_id, $user_name, $phenotype_store_config)) { + $c->stash->{rest} = { success => 1 }; + } else { + $c->stash->{rest} = { error => "An error occurred uploading subplots." }; } + my $layout = $c->stash->{trial_layout}; + $layout->generate_and_cache_layout(); + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); - - $c->stash->{rest} = { success => 1 }; } sub trial_plot_gps_upload : Chained('trial') PathPart('upload_plot_gps') Args(0) { @@ -2342,12 +2602,20 @@ sub trial_treatments : Chained('trial') PathPart('treatments') Args(0) { my $trial = $c->stash->{trial}; - my $data = $trial->get_treatments(); + my $data; + + eval { + $data = $trial->get_treatments(); + }; + + if ($@) { + $c->stash->{rest} = {error => $@}; + } - $c->stash->{rest} = { treatments => $data }; + $c->stash->{rest} = { data => encode_json($data)}; } -sub trial_add_treatment : Chained('trial') PathPart('add_treatment') Args(0) { +sub trial_add_treatment : Chained('trial') PathPart('add_treatment') Args(0) { # DEPRECATED, TREATMENTS ADDED BY PHENO UPLOAD my $self = shift; my $c = shift; @@ -2391,11 +2659,16 @@ sub trial_add_treatment : Chained('trial') PathPart('add_treatment') Args(0) { } } -sub trial_remove_treatment : Chained('trial') PathPart('remove_treatment') Args(0) { +sub trial_remove_treatment : Chained('trial') PathPart('remove_treatment') Args(0) { my $self = shift; my $c = shift; my $treatment_id = $c->req->param('treatment_id'); + if (!$treatment_id) { + $c->stash->{rest} = {errpr => "You need to supply a valid treatment id."}; + return; + } + if (!($c->user()->check_roles('curator'))) { $c->stash->{rest} = { error => 'You do not have the privileges to remove a treatment from this trial.'}; return; @@ -2403,16 +2676,9 @@ sub trial_remove_treatment : Chained('trial') PathPart('remove_treatment') Args( my $trial = $c->stash->{trial}; my $trial_id = $c->stash->{trial_id}; - my $result; - eval { - $result = $trial->remove_treatment($treatment_id); - }; - if ($@) { - $c->stash->{rest} = { error => "An error occurred while removing the treatment: $@" }; - return; - } - if ($result->{error}) { - $c->stash->{rest} = { error => $result->{error} }; + my $delete_trait_return_error = $trial->delete_assayed_trait($c->config->{basepath}, $c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, undef, [], [$treatment_id] ); + if ($delete_trait_return_error) { + $c->stash->{rest} = { error => $delete_trait_return_error }; return; } @@ -2909,7 +3175,7 @@ sub create_plant_plot_entries : Chained('trial') PathPart('create_plant_entries' my $plant_owner = $c->user->get_object->get_sp_person_id; my $plant_owner_username = $c->user->get_object->get_username; my $plants_per_plot = $c->req->param("plants_per_plot") || 8; - my $inherits_plot_treatments = $c->req->param("inherits_plot_treatments"); + my $inherits_plot_treatments = 1; #$c->req->param("inherits_plot_treatments"); my $include_plant_coordinates = $c->req->param('include_plant_coordinates'); my $num_rows = $c->req->param('rows_per_plot'); my $num_cols = $c->req->param('cols_per_plot'); @@ -2929,9 +3195,40 @@ sub create_plant_plot_entries : Chained('trial') PathPart('create_plant_entries' } my $user_id = $c->user->get_object->get_sp_person_id(); - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); + my $user_name = $c->user->get_object->get_username(); + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); + + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $time = DateTime->now(); + my $timestamp = $time->ymd()."_".$time->hms(); - my @plant_entity_params = ($plants_per_plot, $plants_with_treatments, $user_id, $plant_owner_username); + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } + }; + my @plant_entity_params = ($plants_per_plot, $inherits_plot_treatments, $user_id, $plant_owner_username); if ($include_plant_coordinates) { @@ -2947,9 +3244,15 @@ sub create_plant_plot_entries : Chained('trial') PathPart('create_plant_entries' push @plant_entity_params, $num_rows; push @plant_entity_params, $num_cols; + } else { + push @plant_entity_params, undef; + push @plant_entity_params, undef; } + push @plant_entity_params, $phenotype_store_config; + if ($t->create_plant_entities(@plant_entity_params)) { + my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); @@ -2970,7 +3273,7 @@ sub create_plant_subplot_entries : Chained('trial') PathPart('create_plant_subpl my $plant_owner = $c->user->get_object->get_sp_person_id; my $plant_owner_username = $c->user->get_object->get_username; my $plants_per_subplot = $c->req->param("plants_per_subplot") || 8; - my $inherits_plot_treatments = $c->req->param("inherits_plot_treatments"); + my $inherits_plot_treatments = 1; #$c->req->param("inherits_plot_treatments"); my $include_plant_coordinates = $c->req->param('include_plant_coordinates'); my $num_rows = $c->req->param('rows_per_plot'); my $num_cols = $c->req->param('cols_per_plot'); @@ -2990,9 +3293,40 @@ sub create_plant_subplot_entries : Chained('trial') PathPart('create_plant_subpl } my $user_id = $c->user->get_object->get_sp_person_id(); - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); + my $user_name = $c->user->get_object->get_username(); + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); + + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); - my @subplot_plant_entity_params = ($plants_per_subplot, $plants_with_treatments, $user_id, $plant_owner_username); + my $time = DateTime->now(); + my $timestamp = $time->ymd()."_".$time->hms(); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } + }; + my @subplot_plant_entity_params = ($plants_per_subplot, $inherits_plot_treatments, $user_id, $plant_owner_username); if ($include_plant_coordinates) { @@ -3008,8 +3342,13 @@ sub create_plant_subplot_entries : Chained('trial') PathPart('create_plant_subpl push @subplot_plant_entity_params, $num_rows; push @subplot_plant_entity_params, $num_cols; + } else { + push @subplot_plant_entity_params, undef; + push @subplot_plant_entity_params, undef; } + push @subplot_plant_entity_params, $phenotype_store_config; + if ($t->create_plant_subplot_entities(@subplot_plant_entity_params)) { my $dbh = $c->dbc->dbh(); @@ -3031,7 +3370,7 @@ sub create_subplot_entries : Chained('trial') PathPart('create_subplot_entries') my $subplot_owner = $c->user->get_object->get_sp_person_id; my $subplot_owner_username = $c->user->get_object->get_username; my $subplots_per_plot = $c->req->param("subplots_per_plot") || 4; - my $inherits_plot_treatments = $c->req->param("inherits_plot_treatments"); + my $inherits_plot_treatments = 1; #$c->req->param("inherits_plot_treatments"); my $subplots_with_treatments; if($inherits_plot_treatments eq '1'){ $subplots_with_treatments = 1; @@ -3048,9 +3387,40 @@ sub create_subplot_entries : Chained('trial') PathPart('create_subplot_entries') } my $user_id = $c->user->get_object->get_sp_person_id(); - my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); + my $user_name = $c->user->get_object->get_username(); + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); - if ($t->create_subplot_entities($subplots_per_plot, $subplots_with_treatments, $user_id)) { + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $time = DateTime->now(); + my $timestamp = $time->ymd()."_".$time->hms(); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } + }; + if ($t->create_subplot_entities($subplots_per_plot, $inherits_plot_treatments, $user_id, $user_name, $phenotype_store_config)) { my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); @@ -3072,7 +3442,7 @@ sub create_tissue_samples : Chained('trial') PathPart('create_tissue_samples') A my $tissue_owner_username = $c->user->get_object->get_username; my $tissues_per_plant = $c->req->param("tissue_samples_per_plant") || 3; my $tissue_names = decode_json $c->req->param("tissue_samples_names"); - my $inherits_plot_treatments = $c->req->param("inherits_plot_treatments"); + my $inherits_plot_treatments = 1; #$c->req->param("inherits_plot_treatments"); my $use_tissue_numbers = $c->req->param("use_tissue_numbers"); my $tissues_with_treatments; if($inherits_plot_treatments eq '1'){ @@ -3100,9 +3470,41 @@ sub create_tissue_samples : Chained('trial') PathPart('create_tissue_samples') A } my $user_id = $c->user->get_object->get_sp_person_id(); - my $t = CXGN::Trial->new({ bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), trial_id => $c->stash->{trial_id} }); + my $user_name = $c->user->get_object->get_username(); + my $t = CXGN::Trial->new({ + bcs_schema => $c->dbic_schema("Bio::Chado::Schema", undef, $user_id), + phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema", undef, $user_id), + metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema", undef, $user_id), + trial_id => $c->stash->{trial_id} + }); + + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $time = DateTime->now(); + my $timestamp = $time->ymd()."_".$time->hms(); + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } + }; + if ($t->create_tissue_samples($tissue_names, $inherits_plot_treatments, $use_tissue_numbers, $user_id, $user_name, $phenotype_store_config)) { - if ($t->create_tissue_samples($tissue_names, $inherits_plot_treatments, $use_tissue_numbers, $user_id)) { my $dbh = $c->dbc->dbh(); my $bs = CXGN::BreederSearch->new( { dbh=>$dbh, dbname=>$c->config->{dbname}, } ); my $refresh = $bs->refresh_matviews($c->config->{dbhost}, $c->config->{dbname}, $c->config->{dbuser}, $c->config->{dbpass}, 'stockprop', 'concurrent', $c->config->{basepath}); @@ -3116,62 +3518,76 @@ sub create_tissue_samples : Chained('trial') PathPart('create_tissue_samples') A } +sub get_management_regime : Chained('trial') PathPart('get_management_regime') Args(0) { + my $self = shift; + my $c = shift; + my $sp_person_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef; + my $trial_id = $c->stash->{trial_id}; + my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id); + my $t = CXGN::Project->new( { bcs_schema => $schema, trial_id => $trial_id }); + $c->stash->{rest} = {data => encode_json($t->get_management_regime()), success => 1}; +} + sub edit_management_factor_details : Chained('trial') PathPart('edit_management_factor_details') Args(0) { my $self = shift; my $c = shift; my $sp_person_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef; + my $trial_id = $c->stash->{trial_id}; my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id); - my $treatment_date = $c->req->param("treatment_date"); - my $treatment_name = $c->req->param("treatment_name"); - my $treatment_description = $c->req->param("treatment_description"); - my $treatment_type = $c->req->param("treatment_type"); - my $treatment_year = $c->req->param("treatment_year"); + my $management_factor_schedule = $c->req->param("schedule"); + my $management_factor_description = $c->req->param("description"); + my $management_factor_type = $c->req->param("type"); + my $management_factor_completions = decode_json($c->req->param("completions")); #optional + my $start_date = $c->req->param("start_date"); + my $end_date = $c->req->param("end_date"); + my $action = $c->req->param("action"); if (my $error = $self->privileges_denied($c)) { $c->stash->{rest} = { error => $error }; return; } - if (!$treatment_name) { - $c->stash->{rest} = { error => 'No treatment name given!' }; + if (!$management_factor_schedule) { + $c->stash->{rest} = { error => 'No schedule given!' }; return; } - if (!$treatment_description) { - $c->stash->{rest} = { error => 'No treatment description given!' }; + if (!$management_factor_description) { + $c->stash->{rest} = { error => 'No description given!' }; return; } - if (!$treatment_date) { - $c->stash->{rest} = { error => 'No treatment date given!' }; + if (!$management_factor_type) { + $c->stash->{rest} = { error => 'No type given!' }; return; } - if (!$treatment_type) { - $c->stash->{rest} = { error => 'No treatment type given!' }; - return; - } - if (!$treatment_year) { - $c->stash->{rest} = { error => 'No treatment year given!' }; + if (!$action) { + $c->stash->{rest} = { error => 'Server error! Somebody tried sending bad data.' }; return; } - my $t = CXGN::Trial->new( { bcs_schema => $schema, trial_id => $c->stash->{trial_id} }); - my $trial_name = $t->get_name(); + my $t = CXGN::Project->new( { bcs_schema => $schema, trial_id => $trial_id }); - if ($trial_name ne $treatment_name) { - my $trial_rs = $schema->resultset('Project::Project')->search({name => $treatment_name}); - if ($trial_rs->count() > 0) { - $c->stash->{rest} = { error => 'Please use a different treatment name! That name is already in use.' }; - return; + my $management_factor = { + description => $management_factor_description, + schedule => $management_factor_schedule, + type => $management_factor_type, + completions => $management_factor_completions, + start_date => $start_date, + end_date => $end_date + }; + + eval { + if ($action eq "add") { + $t->add_management_factor($management_factor); + } elsif ($action eq "remove") { + $t->remove_management_factor($management_factor); } + }; + if ($@) { + $c->stash->{rest} = {error => "Error editing management regime. $@"}; + } else { + $c->stash->{rest} = { success => 1 }; } - - $t->set_name($treatment_name); - $t->set_management_factor_date($treatment_date); - $t->set_management_factor_type($treatment_type); - $t->set_description($treatment_description); - $t->set_year($treatment_year); - - $c->stash->{rest} = { success => 1 }; } sub privileges_denied { @@ -6017,6 +6433,7 @@ sub add_additional_stocks_for_greenhouse_POST : Args(0) { my $number_of_plants_array = decode_json $number_of_plants_json; my $user_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef; my $schema = $c->dbic_schema('Bio::Chado::Schema', undef, $user_id); + my $metadata_schema = $c->dbic_schema('CXGN::Metadata::Schema', undef, $user_id); if (!$c->user()) { $c->res->redirect( uri( path => '/user/login', query => { goto_url => $c->req->uri->path_query } ) ); @@ -6036,7 +6453,38 @@ sub add_additional_stocks_for_greenhouse_POST : Args(0) { return; } - my $trial = CXGN::Trial->new( { bcs_schema => $schema, trial_id => $trial_id}); + my $temp_basedir = $c->config->{tempfiles_subdir}; + my $site_basedir = $c->config->{basepath}; + if (! -d "$site_basedir/$temp_basedir/delete_nd_experiment_ids/"){ + mkdir("$site_basedir/$temp_basedir/delete_nd_experiment_ids/"); + } + my (undef, $tempfile) = tempfile("$site_basedir/$temp_basedir/delete_nd_experiment_ids/fileXXXX"); + + my $time = DateTime->now(); + my $timestamp = $time->ymd()."_".$time->hms(); + + my $dbh = $c->dbc->dbh(); + + my $p = CXGN::People::Person->new($dbh, $user_id); + my $user_name = $p->get_username; + + my $phenotype_store_config = { + basepath => "$site_basedir/$temp_basedir", + dbhost => $c->config->{dbhost}, + dbuser => $c->config->{dbuser}, + dbname => $c->config->{dbname}, + dbpass => $c->config->{dbpass}, + temp_file_nd_experiment_id => $tempfile, + user_id => $user_id, + metadata_hash => { + archived_file => 'none', + archived_file_type => 'new stock treatment auto inheritance', + operator => $user_name, + date => $timestamp + } + }; + + my $trial = CXGN::Trial->new( { bcs_schema => $schema, trial_id => $trial_id, metadata_schema => $metadata_schema}); my $result; if ($addition_type eq 'new_accessions') { eval { @@ -6052,7 +6500,7 @@ sub add_additional_stocks_for_greenhouse_POST : Args(0) { } } elsif ($addition_type eq 'additional_plants') { eval { - $result = $trial->add_additional_plants_for_greenhouse($stock_list, $number_of_plants_array, $user_id); + $result = $trial->add_additional_plants_for_greenhouse($stock_list, $number_of_plants_array, $user_id, $phenotype_store_config); }; if ($@) { $c->stash->{rest} = { error => "An error occurred while adding additional plants: $@" }; diff --git a/lib/SGN/Controller/Cvterm.pm b/lib/SGN/Controller/Cvterm.pm index 5c4441111c..c6f29f3198 100644 --- a/lib/SGN/Controller/Cvterm.pm +++ b/lib/SGN/Controller/Cvterm.pm @@ -46,10 +46,20 @@ sub view_cvterm : Chained('get_cvterm') PathPart('view') Args(0) { my $props = $self->_cvtermprops($cvterm); my $editable_cvterm_props = "trait_format,trait_default_value,trait_minimum,trait_maximum,trait_details,trait_categories,trait_repeat_type"; + my $allow_trait_edits = 0; + if ($c->config->{allow_trait_edits}) { + $allow_trait_edits = 1; + } + my $allow_treatment_edits = 0; + if ($c->config->{allow_treatment_edits}) { + $allow_treatment_edits = 1; + } $c->stash( template => '/chado/cvterm.mas', cvterm => $cvterm, #deprecate this maybe? + allow_trait_edits => $allow_trait_edits, + allow_treatment_edits => $allow_treatment_edits, cvtermref => { cvterm => $bcs_cvterm, curator => $curator, diff --git a/lib/SGN/Controller/Ontology.pm b/lib/SGN/Controller/Ontology.pm index bb40806293..62c6e9f2d9 100644 --- a/lib/SGN/Controller/Ontology.pm +++ b/lib/SGN/Controller/Ontology.pm @@ -20,6 +20,7 @@ sub onto_browser : Path('/tools/onto') :Args(0) { my $c = shift; my $root_nodes = $c->config->{onto_root_namespaces}; + print STDERR $root_nodes, "\n\n"; my @namespaces = split ",", $root_nodes; foreach my $n (@namespaces) { $n =~ s/\s*(\w+)\s*\(.*\)/$1/g; @@ -34,7 +35,7 @@ sub onto_browser : Path('/tools/onto') :Args(0) { } -sub compose_trait : Path('/tools/compose') :Args(0) { +sub compose_trait : Path('/tools/compose_trait') :Args(0) { my $self = shift; my $c = shift; @@ -120,4 +121,90 @@ sub compose_trait : Path('/tools/compose') :Args(0) { } +sub compose_treatment : Path('/tools/compose_treatment') :Args(0) { + my $self = shift; + my $c = shift; + + if (!$c->user()) { + # redirect to login page + $c->res->redirect( uri( path => '/user/login', query => { goto_url => $c->req->uri->path_query } ) ); + return; + } + my $variable_conf = $c->config->{composable_variables}; + my @composable_cvs = split ",", $c->config->{composable_cvs}; + my $dbh = $c->dbc->dbh(); + my $sp_person_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef; + my $onto = CXGN::Onto->new( { schema => $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado', $sp_person_id) } ); + my %html_hash; + foreach my $name (@composable_cvs) { + $name =~ s/^\s+|\s+$//g; # remove whitespace + if ($name eq 'time' || $name eq 'tod' || $name eq 'toy' || $name eq 'gen' || $name eq 'evt' ) { + print STDERR "Skipping time-related cv\n"; + next; + } + my $cv_type = $name."_ontology"; + #print STDERR "cv_type = $cv_type\n"; + + + my @root_nodes = $onto->get_root_nodes($cv_type); + #print STDERR Dumper \@root_nodes; + if (scalar @root_nodes > 1) { + #create simple selectbox of root_nodes + my $id = $name."_root_select"; + my $name = $name."_root_select"; + my $html = simple_selectbox_html( + name => $name, + id => $id, + choices => \@root_nodes, + class => "form-control", + size => '10', + default => 'Pick an Ontology' + ); + #put html in hash + $html_hash{$cv_type} = $html; + } + else { + my $cv_id = $root_nodes[0][0]; + my @components; + if ($name eq 'experiment_treatment' && $variable_conf == 1) { + @components = $onto->get_variables($cv_id); + } else { + @components = $onto->get_terms($cv_id); + } + my $id = $name."_select"; + my $name = $name."_select"; + my $default = 0; + if ($default) { unshift @components, [ '', $default ]; } + + my $html = simple_selectbox_html( + name => $name, + multiple => 1, + id => $id, + choices => \@components, + size => '10' + ); + #put html in hash + $html_hash{$cv_type} = $html; + } + } + + $c->stash->{object_select} = $html_hash{'object_ontology'}; + $c->stash->{attribute_select} = $html_hash{'attribute_ontology'}; + $c->stash->{method_select} = $html_hash{'method_ontology'}; + $c->stash->{unit_select} = $html_hash{'unit_ontology'}; + $c->stash->{treatment_select} = $html_hash{'experiment_treatment_ontology'}; + $c->stash->{meta_select} = $html_hash{'meta_ontology'}; + + $c->stash->{composable_cvs} = $c->config->{composable_cvs}; + $c->stash->{composable_cvs_allowed_combinations} = $c->config->{composable_cvs_allowed_combinations}; + $c->stash->{composable_tod_root_cvterm} = $c->config->{composable_tod_root_cvterm}; + $c->stash->{composable_toy_root_cvterm} = $c->config->{composable_toy_root_cvterm}; + $c->stash->{composable_gen_root_cvterm} = $c->config->{composable_gen_root_cvterm}; + $c->stash->{composable_evt_root_cvterm} = $c->config->{composable_evt_root_cvterm}; + $c->stash->{composable_meta_root_cvterm} = $c->config->{composable_meta_root_cvterm}; + $c->stash->{user} = $c->user(); + $c->stash->{template} = '/ontology/compose_treatment.mas'; + +} + 1; diff --git a/lib/SGN/Controller/People.pm b/lib/SGN/Controller/People.pm index 2354d9aa86..7baeecc542 100644 --- a/lib/SGN/Controller/People.pm +++ b/lib/SGN/Controller/People.pm @@ -129,7 +129,18 @@ sub people_top_level : Path('/solpeople/profile') Args(1) { $c->stash->{user_status} = $user_info->{$user_type}; } + my $allow_trait_edits = 0; + if ($c->config->{allow_trait_edits}) { + $allow_trait_edits = 1; + } + my $allow_treatment_edits = 0; + if ($c->config->{allow_treatment_edits}) { + $allow_treatment_edits = 1; + } + $c->stash->{site_name} = $c->config->{project_name}; + $c->stash->{allow_trait_edits} = $allow_trait_edits; + $c->stash->{allow_treatment_edits} = $allow_treatment_edits; $c->stash->{user_roles} = \%roles_hash; $c->stash->{sp_person_id} = $p->get_sp_person_id; $c->stash->{username} = $c->user->get_object->get_username; diff --git a/lib/SGN/Controller/Search/Treatment.pm b/lib/SGN/Controller/Search/Treatment.pm new file mode 100644 index 0000000000..facea79489 --- /dev/null +++ b/lib/SGN/Controller/Search/Treatment.pm @@ -0,0 +1,13 @@ +package SGN::Controller::Search::Treatment; + +use Moose; + +BEGIN { extends 'Catalyst::Controller'; } + +sub trait_search_page : Path('/search/treatments/') Args(0) { + my $self = shift; + my $c = shift; + $c->stash->{template} = '/search/treatments.mas'; +} + +1; \ No newline at end of file diff --git a/lib/SGN/Controller/Trait.pm b/lib/SGN/Controller/Trait.pm new file mode 100644 index 0000000000..58499b6678 --- /dev/null +++ b/lib/SGN/Controller/Trait.pm @@ -0,0 +1,47 @@ +package SGN::Controller::Trait; + +use Moose; +use Data::Dumper; +use CXGN::Onto; +use CXGN::Cvterm; + +BEGIN { extends 'Catalyst::Controller'; } + +sub treatment_design_page : Path('/traits/design/') Args(0) { + my $self = shift; + my $c = shift; + + 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); + + if (! $c->config->{allow_trait_edits}) { + $c->stash->{template} = '/site/error/permission_denied.mas'; + } else { + + if ($c->user() && $c->user->check_roles('curator')) { + my $ontology_obj = CXGN::Onto->new({ + schema => $schema + }); + my @root_nodes = $ontology_obj->get_root_nodes('trait_ontology'); + + my $root_term_name = $root_nodes[0]->[1] =~ s/\w+:\d+ //r; + my $db_name = $root_nodes[0]->[1] =~ s/:.*//r; + + my $cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + name => $root_term_name, + cv_id => $root_nodes[0]->[0] + })->cvterm_id(); + + my $cvterm = CXGN::Cvterm->new({ schema=>$schema, cvterm_id => $cvterm_id } ); + $c->stash( + template => '/tools/trait_designer.mas', + trait_root => $cvterm, + db_name => $db_name + ); + } else { + $c->stash->{template} = '/site/error/permission_denied.mas'; + } + } +} + +1; \ No newline at end of file diff --git a/lib/SGN/Controller/Treatment.pm b/lib/SGN/Controller/Treatment.pm new file mode 100644 index 0000000000..4c6df415dc --- /dev/null +++ b/lib/SGN/Controller/Treatment.pm @@ -0,0 +1,47 @@ +package SGN::Controller::Treatment; + +use Moose; +use CXGN::Cvterm; +use CXGN::Onto; + +BEGIN { extends 'Catalyst::Controller'; } + +sub treatment_design_page : Path('/treatments/design/') Args(0) { + my $self = shift; + my $c = shift; + + 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); + + if (! $c->config->{allow_treatment_edits}) { + $c->stash->{template} = '/site/error/permission_denied.mas'; + } else { + + if ($c->user() && $c->user->check_roles('curator')) { + my $ontology_obj = CXGN::Onto->new({ + schema => $schema + }); + my @root_nodes = $ontology_obj->get_root_nodes('experiment_treatment_ontology'); + + my $root_term_name = $root_nodes[0]->[1] =~ s/\w+:\d+ //r; + my $db_name = $root_nodes[0]->[1] =~ s/:.*//r; + + my $cvterm_id = $schema->resultset("Cv::Cvterm")->find({ + name => $root_term_name, + cv_id => $root_nodes[0]->[0] + })->cvterm_id(); + my $cvterm = CXGN::Cvterm->new({ schema=>$schema, cvterm_id => $cvterm_id } ); + $c->stash( + template => '/tools/treatment_designer.mas', + exp_treatment_root => $cvterm, + db_name => $db_name + ); + } else { + $c->stash->{template} = '/site/error/permission_denied.mas'; + } + + } + +} + +1; \ No newline at end of file diff --git a/lib/SGN/View/Trial.pm b/lib/SGN/View/Trial.pm index 2dc806db67..c0c0f2d909 100644 --- a/lib/SGN/View/Trial.pm +++ b/lib/SGN/View/Trial.pm @@ -269,7 +269,7 @@ sub design_layout_view { return "$design_result_html"; } -sub design_info_view { +sub design_info_view { #TODO: fix treatments here my $design_ref = shift; my $design_info_ref = shift; my $trial_stock_type = shift; @@ -354,9 +354,8 @@ sub design_info_view { } if($key eq 'treatments'){ - while(my($k,$v) = each %{$design{$key}}){ - my $treatment_units = join ',', @{$v}; - $treatment_info_string .= "$k: $treatment_units
    "; + while(my($k,$v) = each %{$design{$key}->{'treatments'}}){ + $treatment_info_string .= " - $k
    "; } } } @@ -387,7 +386,13 @@ sub design_info_view { $design_info_html .= "
    Number of reps
    ".scalar(keys %rep_hash)."
    "; } - $design_info_html .= "
    Treatments:
    $treatment_info_string
    "; + if ($treatment_info_string) { + $design_info_html .= "
    Treatments:
    $treatment_info_string
    "; + } else { + $design_info_html .= "
    Treatments:
    None added. Treatments and management regimes can be added on the trial detail page.
    "; + } + + $design_info_html .= ""; diff --git a/mason/analyses/analysis_calculate_statistics.mas b/mason/analyses/analysis_calculate_statistics.mas index 6cefc4ac5b..39a590c2a6 100644 --- a/mason/analyses/analysis_calculate_statistics.mas +++ b/mason/analyses/analysis_calculate_statistics.mas @@ -1,4 +1,4 @@ -analysis_calculate_statistics_select<%args> +<%args> diff --git a/mason/breeders_toolbox/trial.mas b/mason/breeders_toolbox/trial.mas index 125b2eb032..c73c5c75e0 100644 --- a/mason/breeders_toolbox/trial.mas +++ b/mason/breeders_toolbox/trial.mas @@ -185,7 +185,16 @@ $project_id => undef <& /page/detail_page_2_col_section.mas, trial_id => $trial_id, has_col_and_row_numbers => $has_col_and_row_numbers, has_plant_entries => $has_plant_entries, has_subplot_entries => $has_subplot_entries, trial_stock_type => $trial_stock_type, info_section_title => "

    Field Layout Tools and Phenotype Heatmap

    ", info_section_subtitle => $subtitle, buttons_html => $layout_buttons, icon_class => "glyphicon glyphicon-th", info_section_id => "pheno_heatmap" &> -<& /page/detail_page_2_col_section.mas, trial_id => $trial_id, trial_name => $trial_name, info_section_title => "

    Experimental Design

    ", info_section_subtitle => 'View and add experimental design information. Add plant entries and tissue sample entries.', buttons_html => $design_section_buttons, icon_class => "glyphicon glyphicon-list-alt", info_section_id => "trial_design_section", has_plant_entries => $has_plant_entries, has_subplot_entries => $has_subplot_entries, has_tissue_sample_entries => $has_tissue_sample_entries, trial_stock_type => $trial_stock_type &> +% my $experimental_design_buttons = ''; +% if ($user_can_modify) { +% $experimental_design_buttons .= ' '; +% if ($design_name eq 'greenhouse'){ +% $experimental_design_buttons .= ''; +% } +% } +<& /page/detail_page_2_col_section.mas, trial_id => $trial_id, trial_name => $trial_name, info_section_title => "

    Experimental Design

    ", info_section_subtitle => 'View and add experimental design information. Add plant entries and tissue sample entries.', +buttons_html => $experimental_design_buttons, +icon_class => "glyphicon glyphicon-list-alt", info_section_id => "trial_design_section", has_plant_entries => $has_plant_entries, has_subplot_entries => $has_subplot_entries, has_tissue_sample_entries => $has_tissue_sample_entries, trial_stock_type => $trial_stock_type, management_factor_types => $management_factor_types, user_can_modify => $user_can_modify &> <& /page/detail_page_2_col_section.mas, trial_id => $trial_id, info_section_title => "

    Phenotype Raw Data

    ", info_section_subtitle => 'View phenotyping raw data.', info_section_id => "trial_raw_data", trial_name => $trial_name, site_project_name => $site_project_name, sgn_session_id => $sgn_session_id, user_name => $user_name, main_production_site_url => $main_production_site_url, trial_stock_type => $trial_stock_type &> @@ -232,10 +241,11 @@ $project_id => undef <& /breeders_toolbox/trial/download_layout_dialog.mas, trial_id => $trial_id, trial_name => $trial_name, has_subplot_entries => $has_subplot_entries &> <& /breeders_toolbox/trial/add_trial_used_seedlots.mas, trial_id => $trial_id &> <& /breeders_toolbox/trial/upload_plot_gps_dialogs.mas, trial_id => $trial_id &> +<& /breeders_toolbox/trial/add_management_factor_dialog.mas, management_factor_types => $management_factor_types, trial_id => $trial_id &> <& /breeders_toolbox/trial/add_subplots_per_plot.mas, trial_id => $trial_id, trial_name => $trial_name &> <& /breeders_toolbox/trial/add_plants_per_plot.mas, trial_id => $trial_id, trial_name => $trial_name &> <& /breeders_toolbox/trial/add_plants_per_subplot.mas, trial_id => $trial_id, trial_name => $trial_name &> -<& /breeders_toolbox/trial/add_trial_treatment_dialogs.mas, management_factor_types => $management_factor_types &> +<& /breeders_toolbox/trial/add_trial_treatment_dialogs.mas &> <& /breeders_toolbox/trial/change_plot_accessions_dialogs.mas, trial_id => $trial_id &> <& /breeders_toolbox/trial/download_phenotypes_dialog.mas, trial_ids => $trial_id, dialog_name => $trial_name, dialog_type => 'Trial' &> <& /breeders_toolbox/trial/update_experimental_design_type.mas, trial_id => $trial_id, trial_name => $trial_name, design_name => $design_name &> @@ -329,7 +339,7 @@ jQuery(document).ready(function () { button_label = 'Add Additional Accessions or Plants'; } - document.getElementById('trial_detail_page_add_additional_stocks').innerHTML = button_label; + jQuery('#trial_detail_page_add_additional_stocks').text(button_label); }); diff --git a/mason/breeders_toolbox/trial/add_management_factor_dialog.mas b/mason/breeders_toolbox/trial/add_management_factor_dialog.mas new file mode 100644 index 0000000000..d7da2b2cef --- /dev/null +++ b/mason/breeders_toolbox/trial/add_management_factor_dialog.mas @@ -0,0 +1,124 @@ +<%args> +$management_factor_types => undef +$trial_id => undef + + + + + \ No newline at end of file diff --git a/mason/breeders_toolbox/trial/add_plants_per_plot.mas b/mason/breeders_toolbox/trial/add_plants_per_plot.mas index 9e3d7e69e7..c997a18343 100644 --- a/mason/breeders_toolbox/trial/add_plants_per_plot.mas +++ b/mason/breeders_toolbox/trial/add_plants_per_plot.mas @@ -16,20 +16,20 @@ $trial_name => undef
    - -
    + +
    - -
    + +
    - -
    + +
    -
    +
    diff --git a/mason/breeders_toolbox/trial/add_plants_per_subplot.mas b/mason/breeders_toolbox/trial/add_plants_per_subplot.mas index 116a5c7473..20e06e2997 100644 --- a/mason/breeders_toolbox/trial/add_plants_per_subplot.mas +++ b/mason/breeders_toolbox/trial/add_plants_per_subplot.mas @@ -16,20 +16,20 @@ $trial_name => undef
    - -
    + +
    - -
    + +
    - -
    + +