Skip to content

Commit f59ba6e

Browse files
authoredJan 25, 2025··
Merge pull request #5191 from solgenomics/topic/quality_control
Phenotype Quality Control
2 parents 8518f7a + 4a3fbb2 commit f59ba6e

18 files changed

+2743
-381
lines changed
 

‎db/00187/AddBreederRole.pm

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/usr/bin/env perl
2+
3+
4+
=head1 NAME
5+
6+
AddBreederRole
7+
8+
=head1 SYNOPSIS
9+
10+
mx-run AddBreederRole [options] -H hostname -D dbname -u username [-F]
11+
12+
this is a subclass of L<CXGN::Metadata::Dbpatch>
13+
see the perldoc of parent class for more details.
14+
15+
=head1 DESCRIPTION
16+
17+
This patch adds vendor role on sgn_people.sp_roles
18+
19+
This subclass uses L<Moose>. The parent class uses L<MooseX::Runnable>
20+
21+
=head1 AUTHOR
22+
23+
Chris Simoes < ccs263@cornell.edu >
24+
25+
=head1 COPYRIGHT & LICENSE
26+
27+
Copyright 2010 Boyce Thompson Institute for Plant Research
28+
29+
This program is free software; you can redistribute it and/or modify
30+
it under the same terms as Perl itself.
31+
32+
=cut
33+
34+
35+
package AddBreederRole;
36+
37+
use Moose;
38+
39+
extends 'CXGN::Metadata::Dbpatch';
40+
41+
42+
has '+description' => ( default => <<'' );
43+
This patch adds vendor role on sgn_people.sp_roles
44+
45+
has '+prereq' => (
46+
default => sub {
47+
[],
48+
},
49+
);
50+
51+
sub patch {
52+
my $self=shift;
53+
54+
print STDOUT "Executing the patch:\n " . $self->name . ".\n\nDescription:\n ". $self->description . ".\n\nExecuted by:\n " . $self->username . " .";
55+
56+
print STDOUT "\nChecking if this db_patch was executed before or if previous db_patches have been executed.\n";
57+
58+
print STDOUT "\nExecuting the SQL commands.\n";
59+
60+
61+
62+
$self->dbh->do(<<EOSQL);
63+
--do your SQL here
64+
--
65+
INSERT INTO sgn_people.sp_roles(name) VALUES ('breeder');
66+
67+
EOSQL
68+
69+
70+
print "You're done!\n";
71+
72+
}
73+
74+
75+
####
76+
1; #
77+
####
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env perl
2+
3+
4+
=head1 NAME
5+
6+
AddValidatePhenotypeCvterm
7+
8+
=head1 SYNOPSIS
9+
10+
mx-run AddValidadePhenotypeCvterm [options] -H hostname -D dbname -u username [-F]
11+
12+
this is a subclass of L<CXGN::Metadata::Dbpatch>
13+
see the perldoc of parent class for more details.
14+
15+
=head1 DESCRIPTION
16+
This patch adds phenotype_property cvterm for storing a suppressed plot phenotype.
17+
This subclass uses L<Moose>. The parent class uses L<MooseX::Runnable>
18+
19+
=head1 AUTHOR
20+
21+
Chris Simoes <ccs263@cornell.edu>
22+
23+
=head1 COPYRIGHT & LICENSE
24+
25+
Copyright 2010 Boyce Thompson Institute for Plant Research
26+
27+
This program is free software; you can redistribute it and/or modify
28+
it under the same terms as Perl itself.
29+
30+
=cut
31+
32+
33+
package AddValidatePhenotypeCvterm;
34+
35+
use Moose;
36+
use Bio::Chado::Schema;
37+
use Try::Tiny;
38+
extends 'CXGN::Metadata::Dbpatch';
39+
40+
41+
has '+description' => ( default => <<'' );
42+
This patch adds phenotype_property cvterm for storing a suppressed plot phenotype.
43+
44+
has '+prereq' => (
45+
default => sub {
46+
[],
47+
},
48+
49+
);
50+
51+
sub patch {
52+
my $self=shift;
53+
54+
print STDOUT "Executing the patch:\n " . $self->name . ".\n\nDescription:\n ". $self->description . ".\n\nExecuted by:\n " . $self->username . " .";
55+
56+
print STDOUT "\nChecking if this db_patch was executed before or if previous db_patches have been executed.\n";
57+
58+
print STDOUT "\nExecuting the SQL commands.\n";
59+
my $schema = Bio::Chado::Schema->connect( sub { $self->dbh->clone } );
60+
61+
62+
print STDERR "INSERTING CV TERMS...\n";
63+
64+
my $terms = {
65+
'project_property' => [
66+
'validated_phenotype',
67+
]
68+
};
69+
70+
foreach my $t (keys %$terms){
71+
foreach (@{$terms->{$t}}){
72+
$schema->resultset("Cv::Cvterm")->create_with({
73+
name => $_,
74+
cv => $t
75+
});
76+
}
77+
}
78+
79+
80+
print "You're done!\n";
81+
}
82+
83+
84+
####
85+
1; #
86+
####

‎js/source/entries/mixedmodels.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,12 @@ export function init(main_div) {
103103
$('#mixed_model_analysis_prepare_button').click(function () {
104104

105105
dataset_id = get_dataset_id();
106+
var dataset_trait_outliers = $('#dataset_trait_outliers').is(':checked') ? 1 : 0;
107+
106108
if (dataset_id != false) {
107109
$.ajax({
108110
url: '/ajax/mixedmodels/prepare',
109-
data: { 'dataset_id': get_dataset_id() },
111+
data: { 'dataset_id': get_dataset_id(),'dataset_trait_outliers': dataset_trait_outliers, },
110112
success: function (r) {
111113
if (r.error) {
112114
alert(r.error);

‎js/source/entries/qualitycontrol.js

+690
Large diffs are not rendered by default.

‎lib/CXGN/Phenotypes/Search/MaterializedViewTable.pm

+9-3
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,15 @@ sub search {
329329
# push @where_clause, 'JSON_EXISTS(observations, \'$[*] ? (@.value >= '.$self->phenotype_min_value.' && @.value <= '.$self->phenotype_max_value.')\')';
330330
# }
331331
#
332-
#if ($self->exclude_phenotype_outlier){
333-
# push @where_clause, "observations !@> '[{\"outlier\" : 1}]'";;
334-
#}
332+
if ($self->exclude_phenotype_outlier){
333+
push @where_clause, "NOT EXISTS (
334+
SELECT 1
335+
FROM phenotypeprop p
336+
JOIN nd_experiment_phenotype nep ON p.phenotype_id = nep.phenotype_id
337+
JOIN nd_experiment_stock nes ON nes.nd_experiment_id = nep.nd_experiment_id
338+
WHERE nes.stock_id = materialized_phenotype_jsonb_table.observationunit_stock_id
339+
)";
340+
}
335341

336342
my $where_clause = " WHERE " . (join (" AND " , @where_clause));
337343
my $or_clause = '';

‎lib/SGN/Controller/AJAX/Heritability.pm

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ sub shared_phenotypes: Path('/ajax/heritability/shared_phenotypes') : {
5050

5151
my $temppath = $c->config->{basepath}."/".$tempfile;
5252
print STDERR "***** temppath = $temppath\n";
53-
my $ds2 = CXGN::Dataset::File->new(people_schema => $people_schema, schema => $schema, sp_dataset_id => $dataset_id, exclude_dataset_outliers => $exclude_outliers, file_name => $temppath, quotes => 0);
53+
my $ds2 = CXGN::Dataset::File->new(people_schema => $people_schema, schema => $schema, sp_dataset_id => $dataset_id, exclude_dataset_outliers => $exclude_outliers, exclude_phenotype_outlier => $exclude_outliers, file_name => $temppath, quotes => 0);
5454
my $phenotype_data_ref = $ds2->retrieve_phenotypes();
5555

5656
print STDERR Dumper(@trait_info);
@@ -142,7 +142,7 @@ sub generate_results: Path('/ajax/heritability/generate_results') : {
142142
#my $temppath = $heritability_tmp_output . "/" . $tempfile;
143143
my $temppath = $tempfile;
144144

145-
my $ds = CXGN::Dataset::File->new(people_schema => $people_schema, schema => $schema, sp_dataset_id => $dataset_id, exclude_dataset_outliers => $exclude_outliers, file_name => $temppath, quotes => 0);
145+
my $ds = CXGN::Dataset::File->new(people_schema => $people_schema, schema => $schema, sp_dataset_id => $dataset_id, exclude_dataset_outliers => $exclude_outliers, exclude_phenotype_outlier => $exclude_outliers, file_name => $temppath, quotes => 0);
146146

147147
my $phenotype_data_ref = $ds->retrieve_phenotypes($pheno_filepath);
148148

‎lib/SGN/Controller/AJAX/MixedModels.pm

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ sub prepare: Path('/ajax/mixedmodels/prepare') Args(0) {
8787
my $self = shift;
8888
my $c = shift;
8989
my $dataset_id = $c->req->param('dataset_id');
90+
my $exclude_outliers = $c->req->param('dataset_trait_outliers');
9091

9192
if (! $c->user()) {
9293
$c->stash->{rest} = {error=>'You must be logged in first!'};
@@ -101,7 +102,7 @@ sub prepare: Path('/ajax/mixedmodels/prepare') Args(0) {
101102
my $schema = $c->dbic_schema("Bio::Chado::Schema", "sgn_chado");
102103
my $temppath = $c->config->{basepath}."/".$tempfile;
103104

104-
my $ds = CXGN::Dataset::File->new(people_schema => $people_schema, schema => $schema, sp_dataset_id => $dataset_id, file_name => $temppath, quotes => 0);
105+
my $ds = CXGN::Dataset::File->new(people_schema => $people_schema, schema => $schema, sp_dataset_id => $dataset_id,exclude_dataset_outliers => $exclude_outliers, exclude_phenotype_outlier => $exclude_outliers, file_name => $temppath, quotes => 0);
105106
$ds->retrieve_phenotypes();
106107

107108
# Note: file is cleaned by run_model function in CXGN::MixedModel

‎lib/SGN/Controller/AJAX/QualityControl.pm

+355-368
Large diffs are not rendered by default.

‎lib/SGN/Controller/AJAX/Stability.pm

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ sub generate_results: Path('/ajax/stability/generate_results') : {
147147
#my $temppath = $stability_tmp_output . "/" . $tempfile;
148148
my $temppath = $tempfile;
149149

150-
my $ds = CXGN::Dataset::File->new(people_schema => $people_schema, schema => $schema, sp_dataset_id => $dataset_id, exclude_dataset_outliers => $exclude_outliers, file_name => $temppath, quotes => 0);
150+
my $ds = CXGN::Dataset::File->new(people_schema => $people_schema, schema => $schema, sp_dataset_id => $dataset_id, exclude_dataset_outliers => $exclude_outliers, exclude_phenotype_outlier => $exclude_outliers,file_name => $temppath, quotes => 0);
151151

152152
my $phenotype_data_ref = $ds->retrieve_phenotypes($pheno_filepath);
153153

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
2+
package SGN::Controller::AJAX::ValidatedTrials;
3+
4+
use Moose;
5+
6+
use Data::Dumper;
7+
use File::Slurp;
8+
use File::Spec qw | catfile |;
9+
use File::Path qw(rmtree);
10+
use JSON::Any;
11+
use File::Basename qw | basename |;
12+
use DateTime;
13+
use Bio::Chado::Schema;
14+
use CXGN::Dataset::File;
15+
use CXGN::Phenotypes::File;
16+
use CXGN::Phenotypes::PhenotypeMatrix;
17+
use SGN::Controller::AJAX::Dataset;
18+
use CXGN::BreedersToolbox::Projects;
19+
use JSON;
20+
21+
22+
BEGIN { extends 'Catalyst::Controller::REST' };
23+
24+
__PACKAGE__->config(
25+
default => 'application/json',
26+
stash_key => 'rest',
27+
map => { 'application/json' => 'JSON' },
28+
);
29+
30+
31+
32+
sub grab_data :Path('/ajax/validatedtrials/grabdata') Args(0) {
33+
my $self = shift;
34+
my $c = shift;
35+
my $dbh = $c->dbc->dbh();
36+
my $schema = $c->dbic_schema("Bio::Chado::Schema");
37+
38+
39+
40+
my $trials_sql = qq{
41+
SELECT project."name",
42+
nd_geolocation.description as location,
43+
validated_prop.value AS trait_name
44+
FROM project
45+
JOIN projectprop AS validated_prop ON validated_prop.project_id = project.project_id
46+
JOIN projectprop AS location_prop ON location_prop.project_id = project.project_id
47+
JOIN nd_geolocation ON nd_geolocation.nd_geolocation_id = location_prop.value::integer
48+
WHERE validated_prop.type_id = (SELECT cvterm_id FROM cvterm WHERE name = 'validated_phenotype')
49+
AND location_prop.type_id = (SELECT cvterm_id FROM cvterm WHERE name = 'project location')
50+
GROUP BY project."name",location, trait_name;
51+
};
52+
53+
my @validated_projects;
54+
eval {
55+
# Prepare and execute the query
56+
my $sth_trial = $dbh->prepare($trials_sql);
57+
$sth_trial->execute();
58+
59+
# Collect project names from the query result
60+
while (my ($project_name, $project_location, $validated_trait) = $sth_trial->fetchrow_array) {
61+
# Split the validated_trait into two parts using '|' as the separator
62+
my ($trait, $username) = split(/\|/, $validated_trait, 2);
63+
64+
# Store each row's data in a hash reference
65+
my $project_data = {
66+
name => $project_name,
67+
project_location => $project_location,
68+
validated_trait => $trait, # First part
69+
username => $username, # Second part
70+
};
71+
72+
# Push the hash reference into the array
73+
push @validated_projects, $project_data;
74+
}
75+
76+
# print STDERR Dumper \@validated_projects;
77+
78+
};
79+
80+
my $json = JSON->new;
81+
$c->stash->{rest} = { data => \@validated_projects };
82+
83+
# Explicitly encode the response as JSON
84+
my $encoded_response = $json->encode({ data => \@validated_projects });
85+
$c->response->body($encoded_response);
86+
87+
if ($@) {
88+
$c->response->body("Failed to search data: $@");
89+
return;
90+
}
91+
}
92+
93+
sub get_coordinate :Path('/ajax/validatedtrials/getcoordinates') Args(0) {
94+
95+
my ($self, $c) = @_;
96+
97+
my $sp_person_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef;
98+
my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id);
99+
my $dbh = $c->dbc->dbh();
100+
101+
102+
# Parse the input JSON to get the list of location names
103+
my $locations_json = $c->req->param('locNames');
104+
my $location_names = decode_json($locations_json);
105+
106+
# Escape the location names for SQL query
107+
my $placeholders = join(", ", ("?") x @$location_names);
108+
109+
# Prepare and execute the SQL query
110+
my $sql = "
111+
SELECT DISTINCT p.name, p.project_id, ng.latitude, ng.longitude
112+
FROM project p
113+
JOIN nd_experiment_project nep ON nep.project_id = p.project_id
114+
JOIN nd_experiment ne ON ne.nd_experiment_id = nep.nd_experiment_id
115+
JOIN nd_geolocation ng ON ng.nd_geolocation_id = ne.nd_geolocation_id
116+
WHERE p.name IN ($placeholders)
117+
";
118+
119+
120+
eval{
121+
my $sth = $dbh->prepare($sql);
122+
$sth->execute(@{$location_names});
123+
124+
my @locations_with_coordinates;
125+
while (my ($name, $project_id, $latitude, $longitude) = $sth->fetchrow_array) {
126+
push @locations_with_coordinates, { name => $name, trial_id => $project_id, latitude => $latitude, longitude => $longitude };
127+
}
128+
# Return the results as JSON
129+
$c->res->content_type('application/json');
130+
$c->res->body(encode_json(\@locations_with_coordinates));
131+
};
132+
133+
if ($@) {
134+
$c->res->status(500);
135+
$c->res->body(encode_json({ error => "Database error: $_" }));
136+
};
137+
138+
}
139+
140+
sub get_phenotype :Path('/ajax/validatedtrials/getphenotype') Args(0){
141+
142+
my ($self, $c) = @_;
143+
144+
my $sp_person_id = $c->user() ? $c->user->get_object()->get_sp_person_id() : undef;
145+
my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id);
146+
my $dbh = $c->dbc->dbh();
147+
148+
# Parse the input JSON to get the list of trial and trait pairs
149+
my $projects_json = $c->req->param('projectTrait');
150+
my $projects_names = decode_json($projects_json);
151+
152+
# Array to hold final results
153+
my @data;
154+
155+
foreach my $project (@$projects_names) {
156+
my $trial_name = $project->{name};
157+
my $trait_name = $project->{validated_trait};
158+
159+
# Skip if either trial or trait is missing
160+
next unless $trial_name && $trait_name;
161+
162+
# Fetch phenotype data for this trial–trait pair, excluding outliers
163+
my $sql_phenotypes = "
164+
select p.name as location_name, s.uniquename as plot_name, s2.uniquename as accession, cvterm.name as trait, phenotype.value
165+
from phenotype
166+
join nd_experiment_phenotype nep ON nep.phenotype_id = phenotype.phenotype_id
167+
join nd_experiment_project nep2 on nep2.nd_experiment_id = nep.nd_experiment_id
168+
join nd_experiment_stock nes on nes.nd_experiment_id = nep.nd_experiment_id
169+
join phenotype p2 ON p2.phenotype_id = nep.phenotype_id
170+
join project p on p.project_id = nep2.project_id
171+
join cvterm on p2.cvalue_id = cvterm.cvterm_id
172+
join stock s on s.stock_id = nes.stock_id
173+
join stock_relationship sr on sr.subject_id = nes.stock_id
174+
join stock s2 on s2.stock_id = sr.object_id
175+
where p.name = ?
176+
and cvterm.name = ?
177+
and phenotype.phenotype_id not in ( select phenotype_id from phenotypeprop p3 WHERE p3.type_id = ( select cvterm_id from cvterm where name = 'phenotype_outlier'));
178+
";
179+
180+
eval {
181+
my $sth = $dbh->prepare($sql_phenotypes);
182+
$sth->execute($trial_name, $trait_name);
183+
184+
while (my ($location, $plot, $accession, $trait, $value) = $sth->fetchrow_array) {
185+
push @data, {
186+
trial => $location,
187+
plot => $plot,
188+
accession => $accession,
189+
trait => $trait,
190+
value => $value,
191+
};
192+
}
193+
};
194+
195+
# Handle errors
196+
if ($@) {
197+
warn "Error fetching phenotype data for trial '$trial_name' and trait '$trait_name': $@";
198+
}
199+
}
200+
201+
# Return the combined results as JSON
202+
$c->res->content_type('application/json');
203+
$c->res->body(encode_json(\@data));
204+
205+
# Debugging: Print results
206+
# print Dumper \@data;
207+
}
208+
209+
210+
1;

‎lib/SGN/Controller/QualityControl.pm

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
package SGN::Controller::QualityControl;
3+
4+
use Moose;
5+
use URI::FromHash qw | uri |;
6+
7+
BEGIN { extends 'Catalyst::Controller' };
8+
9+
10+
sub quality_control_index :Path('/tools/qualitycontrol') Args(0) {
11+
12+
my $self = shift;
13+
my $c = shift;
14+
15+
if (!$c->user()) {
16+
$c->res->redirect( uri( path => '/user/login', query => { goto_url => $c->req->uri->path_query } ) );
17+
}
18+
19+
$c->stash->{template} = '/tools/qualityControl/dataset_quality_control.mas';
20+
}
21+
22+
1;

‎lib/SGN/Controller/ValidatedTrials.pm

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
package SGN::Controller::ValidatedTrials;
3+
4+
use Moose;
5+
use URI::FromHash qw | uri |;
6+
7+
BEGIN { extends 'Catalyst::Controller' };
8+
9+
10+
sub quality_control_index :Path('/tools/validatedtrials') Args(0) {
11+
12+
my $self = shift;
13+
my $c = shift;
14+
15+
if (!$c->user()) {
16+
$c->res->redirect( uri( path => '/user/login', query => { goto_url => $c->req->uri->path_query } ) );
17+
}
18+
19+
$c->stash->{template} = '/tools/qualityControl/validated_trials.mas';
20+
}
21+
22+
1;

‎mason/tools/heritability/index.mas

+15-4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99
<%perl>
1010
use JSON::Any;
11-
12-
11+
1312
</%perl>
1413

1514

@@ -129,6 +128,17 @@
129128
<script>
130129

131130

131+
var require_login = "<% $c->get_conf('brapi_require_login') %>";
132+
if (require_login === '1'){
133+
auth_token = "<% CXGN::Login->new($c->dbc->dbh)->get_login_cookie() %>";
134+
if (!auth_token){ alert("Login required to display heritability"); }
135+
}
136+
137+
var check_outliers = "<% $c->get_conf('exclude_phenotype_outliers') || 0 %>";
138+
139+
if (parseInt(check_outliers) === 1) {
140+
document.getElementById('dataset_trait_outliers').checked = true;
141+
}
132142

133143
jQuery(document).ready(function() {
134144
if (isLoggedIn()) {
@@ -143,13 +153,14 @@ jQuery(document).ready(function() {
143153
$('#trait_histogram_plot').html('');
144154
$('#heritability_output').empty();
145155
});
146-
156+
147157
jQuery('#selectDataset').click(function() {
148158
var dataset_id = get_dataset_id();
149159
if (!dataset_id) {
150160
return;
151161
}
152-
var dataset_trait_outliers = $('#dataset_trait_outliers').is(':checked') ? 1 : 0;
162+
163+
dataset_trait_outliers = $('#dataset_trait_outliers').is(':checked') ? 1 : 0;
153164
document.getElementById("preview_trait_histogram_btn").disabled = true;
154165
document.getElementById("loading-spinner").style.visibility = 'visible';
155166
$.ajax({

‎mason/tools/mixedmodels.mas

+9
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@
8585
</div>
8686
</div>
8787
<br />
88+
<input type="checkbox" id="dataset_trait_outliers" />
89+
<label>Use validated trials &nbsp; </label>
90+
<br>
8891
<br />
8992

9093
</&>
@@ -242,6 +245,12 @@
242245

243246
<script>
244247

248+
var check_outliers = "<% $c->get_conf('exclude_phenotype_outliers') || 0 %>";
249+
250+
if (parseInt(check_outliers) === 1) {
251+
document.getElementById('dataset_trait_outliers').checked = true;
252+
}
253+
245254
$(document).ready( function() {
246255
var mixedmodels = window.jsMod['mixedmodels'].init("mixedmodels");
247256
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
<%args>
2+
3+
</%args>
4+
5+
<& /util/import_javascript.mas, classes => ['jquery', 'CXGN.BreedersToolbox.HTMLSelect'], entries => ['qualitycontrol' ] &>
6+
7+
<script src="https://cdn.jsdelivr.net/npm/vega@3"></script>
8+
<script src="https://cdn.jsdelivr.net/npm/vega-lite@2"></script>
9+
<script src="https://cdn.jsdelivr.net/npm/vega-embed@3"></script>
10+
11+
12+
<& /page/page_title.mas, title=>"Quality Control" &>
13+
14+
15+
<div id="qualitycontrol_tool">
16+
</div>
17+
18+
19+
<style>
20+
.factor {
21+
z-index:4;
22+
border-style:solid;
23+
border-radius:8px;
24+
width:200px;
25+
height:100;
26+
border-color:#337ab7;
27+
background-color:#337ab7;
28+
color:white;
29+
margin:4px
30+
}
31+
.factor_panel {
32+
min-height:100px;
33+
height:auto;
34+
margin-top:0px;
35+
border-style:dotted;
36+
border-width:5px;
37+
color:grey;
38+
background-color:lightyellow;
39+
}
40+
.factor_interaction_panel {
41+
border-style:dotted;
42+
border-width:0px;
43+
margin-top:20px;
44+
height:auto;
45+
z-index:1;
46+
}
47+
.model_bg {
48+
margin-left:30px;
49+
margin-right:30px;
50+
background-color:#DDEEEE;
51+
min-height:80px;
52+
padding-top:10px;
53+
padding-left:10px;
54+
padding-bottom:10px;
55+
border-radius:8px;
56+
}
57+
58+
#outliers_range .ui-slider-range { background: #5cb85c; }
59+
#outliers_range .ui-slider-handle { border-color: #d9534f; }
60+
#outliers_range { background: #d9534f; margin-top: 0.7em;}
61+
#custom-handle {
62+
width: 3em;
63+
height: 1.6em;
64+
top: 50%;
65+
margin-top: -.8em;
66+
text-align: center;
67+
line-height: 1.6em;
68+
}
69+
70+
</style>
71+
72+
<div id="qualitycontrol_tool">
73+
</div>
74+
75+
76+
<div class="container">
77+
<div class="row">
78+
<div class="col-md-12">
79+
<div class="container-fluid">
80+
<&| /util/workflow.mas, id=> "quality_control_analysis_workflow" &>
81+
<&| /util/workflow.mas:step, title=> "Choose Dataset" &>
82+
<& /page/page_title.mas, title=>"Choose the dataset for QC" &>
83+
<span style="width:240px" id="qc_dataset_select">
84+
</span>
85+
<div>
86+
<div style="float:right">
87+
<button class="btn btn-main" id="qc_analysis_prepare_button" onclick="Workflow.complete(this);">Choose Dataset and continue</button>
88+
</div>
89+
</div>
90+
<br />
91+
<br />
92+
93+
</&>
94+
95+
96+
<&| /util/workflow.mas:step, title=> "Select Trait" &>
97+
<& /page/page_title.mas, title=>"Select a Trait for QC" &>
98+
99+
<!-- Existing checkboxes (dynamic options) -->
100+
<div id="selected_variable" style="margin-bottom:50px">
101+
<label for="trait_select">Select Trait:</label>
102+
<select id="trait_select" name="trait_select" class="form-control">
103+
<option disabled selected value>Select a trait</option>
104+
</select>
105+
</div>
106+
107+
<div id="trait_boxplot">
108+
<br>
109+
<br>
110+
</div>
111+
112+
<div class="col-md-12 well" style="margin-top: 10px;">
113+
<h3>Outliers Range</h3>
114+
<div class="col-md-8">
115+
<div id="outliers_range">
116+
<div id="custom-handle" class="ui-slider-handle"></div>
117+
</div>
118+
</div>
119+
</div>
120+
121+
<hr style="border: 1px solid gray; margin: 20px 0;">
122+
<div class="col-md-12 collapsible">
123+
<div class="content">
124+
<h4><b>Selecting this box, it will set fixed minimum and maximum for all locations.</b></h4>
125+
<br>
126+
<div style="display: flex; align-items: center; gap: 10px; margin-top: 10px;">
127+
<label for="min-limit">Minimum limit:</label>
128+
<input type="number" id="min-limit" name="min-limit" placeholder="Enter minimum" class="form-control" style="width: auto;">
129+
130+
<label for="max-limit">Maximum limit:</label>
131+
<input type="number" id="max-limit" name="max-limit" placeholder="Enter maximum" class="form-control" style="width: auto;">
132+
</div>
133+
<br>
134+
<br>
135+
<div style="display: flex; align-items: center; gap: 10px;">
136+
<button id="fixed-min-max" style="margin-right: 5px;">
137+
Select Fixed Min and Max
138+
</button>
139+
</div>
140+
</div>
141+
<br>
142+
<br>
143+
</div>
144+
<hr style="border: 1px solid gray; margin: 20px 0;">
145+
146+
<div id="outlier_table_container">
147+
<h3>Raw Data Available</h3>
148+
<table id="outlier_table" class="table table-bordered table-striped">
149+
<thead>
150+
<tr>
151+
<th>Location ID</th>
152+
<th>Location Name</th>
153+
<th>Trial Name</th>
154+
<th>Min</th>
155+
<th>Max</th>
156+
<th>Mean</th>
157+
<th>SD</th>
158+
<th>CV</th>
159+
</tr>
160+
</thead>
161+
<tbody></tbody>
162+
</table>
163+
</div>
164+
165+
<div id="cleaned_table_container">
166+
<h3>Data After Filtering</h3>
167+
<table id="clean_table" class="table table-bordered table-striped">
168+
<thead>
169+
<tr>
170+
<th>Location ID</th>
171+
<th>Location Name</th>
172+
<th>Trial Name</th>
173+
<th>Min</th>
174+
<th>Max</th>
175+
<th>Mean</th>
176+
<th>SD</th>
177+
<th>CV</th>
178+
</tr>
179+
</thead>
180+
<tbody></tbody>
181+
</table>
182+
<br>
183+
</div>
184+
<div style="float:right">
185+
<button class="btn btn-main" id="qc_choose_variable" onclick="Workflow.complete(this);">Next step</button>
186+
</div>
187+
</&>
188+
189+
<&| /util/workflow.mas:step, title=> "Other Traits as Outlier" &>
190+
<& /page/page_title.mas, title=>"Set Plot as Outlier for Other Traits" &>
191+
192+
<div style="text-align: center;">
193+
<p>This is an option to set selected plots as outliers for other traits as well.</p>
194+
<p>Clicking on <b>'Select Other Traits'</b> you will see available traits in your dataset.</p>
195+
<p>If you want to ignore this step, please click on <b>'Next step'</b> button.</p>
196+
<button id="select_traits_button">Select Other Traits</button>
197+
<div id="other_traits" style="margin-top: 20px; margin-bottom: 50px;">
198+
<!-- Dynamically populated checkboxes will appear here -->
199+
</div>
200+
<br>
201+
</div>
202+
<div style="float:right">
203+
<button class="btn btn-main" id="qc_other_traits" onclick="Workflow.complete(this);">Next step</button>
204+
</div>
205+
206+
</&>
207+
208+
<&| /util/workflow.mas:step, title=> "Save Outliers" &>
209+
<& /page/page_title.mas, title=>"Save Outliers" &>
210+
<div>
211+
<div style="text-align: center;">
212+
<p>This is the validation process. Trials cleaned of outliers can be used in downstream analysis.</p>
213+
<p>To validate a dataset you will need privilieges as <b>CURATOR</b> or <b>BREEDER</b>.</p>
214+
<p>Only <b>CURATORS</b> are allowed to restore the dataset to original values.</p>
215+
<button class="btn btn-main" id="restore_outliers_button" onclick="Workflow.complete(this);">Restore</button>
216+
<button class="btn btn-main" id="store_outliers_button" onclick="Workflow.complete(this);">Validate</button>
217+
</div>
218+
</&>
219+
<div id="tempfile" style="display:none"></div>
220+
</div>
221+
</div>
222+
</div>
223+
224+
225+
</&>
226+
227+
228+
<script>
229+
230+
$(document).ready( function() {
231+
var qualitycontrol = window.jsMod['qualitycontrol'].init("qualitycontrol");
232+
});
233+
234+
</script>
235+

‎mason/tools/qualityControl/validated_trials.mas

+987
Large diffs are not rendered by default.

‎mason/tools/stability/index.mas

+16-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
<br>
5252
</div>
5353
<div class = "form-group form-inline">
54+
<input type="checkbox" id="dataset_trait_outliers" />
55+
<label>Exclude dataset outliers &nbsp; </label>
56+
<br>
5457
<label for = select_method>Available Methods: </label>
5558
<select class="form-control input-sm" id="select_method">
5659
<option selected value="method_empty"></option>
@@ -135,6 +138,18 @@
135138

136139
<script>
137140

141+
var require_login = "<% $c->get_conf('brapi_require_login') %>";
142+
if (require_login === '1'){
143+
auth_token = "<% CXGN::Login->new($c->dbc->dbh)->get_login_cookie() %>";
144+
if (!auth_token){ alert("Login required to display heritability"); }
145+
}
146+
147+
var check_outliers = "<% $c->get_conf('exclude_phenotype_outliers') || 0 %>";
148+
149+
if (parseInt(check_outliers) === 1) {
150+
document.getElementById('dataset_trait_outliers').checked = true;
151+
}
152+
138153
jQuery(document).ready(function() {
139154
if (isLoggedIn()) {
140155
get_select_box("datasets", "dataset_select", {"checkbox_name":"dataset_select_checkbox"});
@@ -313,7 +328,7 @@ jQuery('#runAMMI').click( function () {
313328
var trait_id = $('#pheno_select').val();
314329
var method_id = $('#select_method').val();
315330
var imput_id = $('#select_imput').val();
316-
var dataset_trait_outliers = 0;
331+
var dataset_trait_outliers = $('#dataset_trait_outliers').is(':checked') ? 1 : 0;
317332
$.ajax({
318333
url: '/ajax/stability/generate_results',
319334
data: {

‎sgn_test.conf

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ main_production_site_url http://localhost
1313
supportedCrop Cassava
1414
preferred_species Manihot esculenta
1515

16+
exclude_phenotype_outliers = 1
17+
1618
# Ontology options
1719
composable_cvs trait,attribute,tod,toy
1820
composable_cvs_allowed_combinations Image|trait+attribute+tod+toy

0 commit comments

Comments
 (0)
Please sign in to comment.