Merge pull request #5205 from solgenomics/topic/generic_trial_upload
[sgn.git] / lib / SGN / Controller / AJAX / Cvterm.pm
blob210a5f541741156ddc79a9e263031a57f68f5841
2 =head1 NAME
4 SGN::Controller::AJAX::Cvterm - a REST controller class to provide the
5 backend for objects linked with cvterms
7 =head1 DESCRIPTION
9 Browse the cvterm database for selecting cvterms (ontology terms, and their evidece codes) to be linked with other objects
11 =head1 AUTHOR
13 Naama Menda <nm249@cornell.edu>
15 =cut
17 package SGN::Controller::AJAX::Cvterm;
19 use Moose;
21 use List::MoreUtils qw /any /;
22 use Try::Tiny;
23 use CXGN::Page::FormattingHelpers qw/ columnar_table_html commify_number /;
24 use CXGN::Chado::Cvterm;
25 use Data::Dumper;
26 use JSON;
28 BEGIN { extends 'Catalyst::Controller::REST' }
30 __PACKAGE__->config(
31 default => 'application/json',
32 stash_key => 'rest',
33 map => { 'application/json' => 'JSON' },
37 =head2 autocomplete
39 Public Path: /ajax/cvterm/autocomplete
41 Autocomplete a cvterm name. Takes a single GET param,
42 C<term>, responds with a JSON array of completions for that term.
44 =cut
46 sub autocomplete : Local : ActionClass('REST') { }
48 sub autocomplete_GET :Args(0) {
49 my ( $self, $c ) = @_;
51 #my $term = $c->req->param('term_name');
52 my $db_name = $c->request->param('db_name');
53 # trim and regularize whitespace
54 #$term =~ s/(^\s+|\s+)$//g;
55 #$term =~ s/\s+/ /g;
56 my $term_name = $c->request->param("term");
58 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
59 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
60 FROM db
61 JOIN dbxref USING (db_id ) JOIN cvterm USING (dbxref_id)
62 JOIN cv USING (cv_id )
63 LEFT JOIN cvtermsynonym USING (cvterm_id )
64 WHERE db.name = ? AND (cvterm.name ilike ? OR cvtermsynonym.synonym ilike ? OR cvterm.definition ilike ?) AND cvterm.is_obsolete = 0 AND is_relationshiptype = 0
65 GROUP BY cvterm.cvterm_id,cv.name, cvterm.name, dbxref.accession, db.name
66 ORDER BY cv.name, cvterm.name limit 30";
67 my $sth= $schema->storage->dbh->prepare($query);
68 $sth->execute($db_name, "\%$term_name\%", "\%$term_name\%", "\%$term_name\%");
69 my @response_list;
70 while (my ($cvterm_id, $cv_name, $cvterm_name, $accession) = $sth->fetchrow_array() ) {
71 push @response_list, $cv_name . "--" . $accession . "--" . $cvterm_name ;
73 $c->stash->{rest} = \@response_list;
76 sub relationships : Local : ActionClass('REST') { }
78 sub relationships_GET :Args(0) {
79 my ($self, $c) = @_;
80 my $relationship_query = $c->dbc->dbh->prepare("SELECT distinct(cvterm.cvterm_id), cvterm.name
81 FROM public.cvterm
82 JOIN public.cv USING (cv_id)
83 JOIN public.cvterm_relationship ON (cvterm.cvterm_id= cvterm_relationship.subject_id)
84 WHERE cv.name ='relationship' AND
85 cvterm.is_obsolete = 0
86 ORDER BY cvterm.name;
87 ");
88 $relationship_query->execute();
89 my $hashref={};
90 while ( my ($cvterm_id, $cvterm_name) = $relationship_query->fetchrow_array() ) {
91 $hashref->{$cvterm_name} = $cvterm_id;
93 $c->stash->{rest} = $hashref;
96 sub locus_relationships : Local : ActionClass('REST') { }
98 sub locus_relationships_GET :Args(0) {
99 my ($self, $c) = @_;
100 my $query = $c->dbc->dbh->prepare(
101 "SELECT distinct(cvterm.cvterm_id), cvterm.name
102 FROM public.cvterm
103 JOIN public.cv USING (cv_id)
104 WHERE cv.name ='Locus Relationship' AND
105 cvterm.is_obsolete = 0
106 ORDER BY cvterm.name;
108 $query->execute();
109 my $hashref={};
110 while ( my ($cvterm_id, $cvterm_name) = $query->fetchrow_array() ) {
111 $hashref->{$cvterm_name} = $cvterm_id;
113 $c->stash->{rest} = $hashref;
116 =head2
118 Public Path: /ajax/cvterm/evidence
120 get a list of available evidence codes from cvterms
121 responds with a JSON array .
123 =cut
125 sub evidence : Local : ActionClass('REST') { }
127 sub evidence_GET :Args(0) {
128 my ($self, $c) = @_;
129 my $query = $c->dbc->dbh->prepare(
130 "SELECT distinct(cvterm.cvterm_id), cvterm.name
131 FROM public.cvterm_relationship
132 JOIN public.cvterm ON (cvterm.cvterm_id= cvterm_relationship.subject_id)
133 WHERE object_id= (select cvterm_id FROM cvterm where name = 'evidence_code')
134 AND cvterm.is_obsolete = 0
135 ORDER BY cvterm.name" );
136 $query->execute();
137 my $hashref={};
138 while ( my ($cvterm_id, $cvterm_name) = $query->fetchrow_array() ) {
139 $hashref->{$cvterm_name} = $cvterm_id;
141 $c->stash->{rest} = $hashref;
145 sub evidence_description : Local : ActionClass('REST') { }
147 sub evidence_description_GET :Args(0) {
148 my ($self, $c) = @_;
149 my $evidence_code_id = $c->request->param("evidence_code_id");
150 my $query = $c->dbc->dbh->prepare("SELECT cvterm_id, cvterm.name FROM cvterm
151 JOIN cvterm_relationship ON cvterm_id=subject_id
152 WHERE object_id= (select cvterm_id FROM public.cvterm WHERE cvterm_id= ?)
153 AND cvterm.is_obsolete = 0"
155 $query->execute($evidence_code_id);
156 my $hashref={};
157 while ( my ($cvterm_id, $cvterm_name) = $query->fetchrow_array() ) {
158 $hashref->{$cvterm_name} = $cvterm_id;
160 $c->stash->{rest} = $hashref;
163 sub get_synonyms : Path('/ajax/cvterm/get_synonyms') Args(0) {
165 my $self = shift;
166 my $c = shift;
167 my @trait_ids = $c->req->param('trait_ids[]');
168 print STDERR "Trait ids = @trait_ids\n";
169 my $dbh = $c->dbc->dbh();
170 my $synonyms = {};
172 foreach my $trait_id (@trait_ids) {
173 my $cvterm = CXGN::Chado::Cvterm->new( $dbh, $trait_id );
174 my $found_cvterm_id = $cvterm->get_cvterm_id;
175 $synonyms->{$trait_id} = $cvterm->get_uppercase_synonym() || "None";
178 $c->stash->{rest} = { synonyms => $synonyms };
182 sub get_annotated_stocks :Chained('/cvterm/get_cvterm') :PathPart('datatables/annotated_stocks') Args(0) {
183 my ($self, $c) = @_;
184 my $cvterm = $c->stash->{cvterm};
185 my $cvterm_id = $cvterm->cvterm_id;
186 my $q = <<'';
187 SELECT DISTINCT
188 type.name,
189 stock_id,
190 stock.uniquename,
191 stock.description
192 FROM cvtermpath
193 JOIN cvterm on (cvtermpath.object_id = cvterm.cvterm_id OR cvtermpath.subject_id = cvterm.cvterm_id )
194 JOIN stock_cvterm on (stock_cvterm.cvterm_id = cvterm.cvterm_id)
195 JOIN stock USING (stock_id)
196 JOIN cvterm as type on type.cvterm_id = stock.type_id
197 WHERE cvtermpath.object_id = ?
198 AND stock.is_obsolete = ?
199 AND pathdistance > 0
200 AND 0 = ( SELECT COUNT(*)
201 FROM stock_cvtermprop p
202 WHERE type_id IN ( SELECT cvterm_id FROM cvterm WHERE name = 'obsolete' )
203 AND p.stock_cvterm_id = stock_cvterm.stock_cvterm_id
204 AND value = '1'
206 ORDER BY stock.uniquename
208 my $sth = $c->dbc->dbh->prepare($q);
209 my $rows = $c->stash->{rest}{count} = 0 + $sth->execute($cvterm_id, 'false');
211 my @stock_data;
212 while ( my ($type, $stock_id , $stock_name, $description) = $sth->fetchrow_array ) {
213 my $stock_link = qq|<a href="/stock/$stock_id/view">$stock_name</a> |;
214 push @stock_data, [
215 $type,
216 $stock_link,
217 $description,
220 $c->stash->{rest} = { data => \@stock_data, };
225 sub get_annotated_loci :Chained('/cvterm/get_cvterm') :PathPart('datatables/annotated_loci') Args(0) {
226 my ($self, $c) = @_;
227 my $cvterm = $c->stash->{cvterm};
228 my $cvterm_id = $cvterm->cvterm_id;
230 my $q = "SELECT DISTINCT locus_id, locus_name, locus_symbol, common_name FROM cvtermpath
231 JOIN cvterm ON (cvtermpath.object_id = cvterm.cvterm_id OR cvtermpath.subject_id = cvterm.cvterm_id)
232 JOIN phenome.locus_dbxref USING (dbxref_id )
233 JOIN phenome.locus USING (locus_id)
234 JOIN sgn.common_name USING (common_name_id)
235 WHERE (cvtermpath.object_id = ?) AND locus_dbxref.obsolete = 'f' AND locus.obsolete = 'f' AND pathdistance > 0";
237 my $sth = $c->dbc->dbh->prepare($q);
238 $sth->execute($cvterm_id);
239 my @data;
240 while ( my ($locus_id, $locus_name, $locus_symbol, $common_name) = $sth->fetchrow_array ) {
241 my $link = qq|<a href="/locus/$locus_id/view">$locus_symbol</a> |;
242 push @data,
245 $common_name,
246 $link,
247 $locus_name,
251 $c->stash->{rest} = { data => \@data, };
256 sub get_phenotyped_stocks :Chained('/cvterm/get_cvterm') :PathPart('datatables/phenotyped_stocks') Args(0) {
257 my ($self, $c) = @_;
258 my $cvterm = $c->stash->{cvterm};
259 my $cvterm_id = $cvterm->cvterm_id;
261 my $q = "SELECT DISTINCT acc.stock_id, pathdistance, acc.uniquename, acc.description, type.name
262 FROM cvtermpath
263 JOIN cvterm ON (cvtermpath.object_id = cvterm.cvterm_id
264 OR cvtermpath.subject_id = cvterm.cvterm_id )
265 JOIN phenotype on cvterm.cvterm_id = phenotype.observable_id
266 JOIN nd_experiment_phenotype USING (phenotype_id)
267 JOIN nd_experiment_stock USING (nd_experiment_id)
268 JOIN stock as plot USING (stock_id)
269 JOIN stock_relationship on(plot.stock_id=stock_relationship.subject_id) join stock as acc on(stock_relationship.object_id=acc.stock_id)
270 JOIN cvterm as type on type.cvterm_id = acc.type_id
271 WHERE cvtermpath.object_id = ? ORDER BY acc.stock_id " ;
274 my $sth = $c->dbc->dbh->prepare($q);
275 $sth->execute($cvterm_id) ;
276 #$c->stash->{rest}{count} = 0 + $sth->execute($cvterm_id);
277 my @data;
278 while ( my ($stock_id, $pathdistance, $stock_name, $description, $type) = $sth->fetchrow_array ) {
279 my $link = qq|<a href="/stock/$stock_id/view">$stock_name</a> |;
280 push @data, [
281 $type,
282 $link,
283 $description,
286 $c->stash->{rest} = { data => \@data, };
289 sub get_direct_trials :Chained('/cvterm/get_cvterm') :PathPart('datatables/direct_trials') Args(0) {
290 my ($self, $c) = @_;
291 my $cvterm = $c->stash->{cvterm};
292 my $cvterm_id = $cvterm->cvterm_id;
293 my $q = "SELECT DISTINCT project_id, project.name, project.description
294 FROM public.project
295 JOIN nd_experiment_project USING (project_id)
296 JOIN nd_experiment_stock USING (nd_experiment_id)
297 JOIN nd_experiment_phenotype USING (nd_experiment_id)
298 JOIN phenotype USING (phenotype_id)
299 JOIN cvterm on cvterm.cvterm_id = phenotype.observable_id
300 WHERE observable_id = ?
303 my $sth = $c->dbc->dbh->prepare($q);
304 my $count = 0 + $sth->execute($cvterm_id );
305 my @data;
306 while ( my ($project_id, $project_name, $description) = $sth->fetchrow_array ) {
307 my $link = qq|<a href="/breeders/trial/$project_id">$project_name</a> |;
308 push @data,
310 $link,
311 $description,
314 $c->stash->{rest} = { data => \@data, count => $count };
317 sub get_cvtermprops : Path('/cvterm/prop/get') : ActionClass('REST') { }
319 sub get_cvtermprops_GET {
320 my ($self, $c) = @_;
322 my $cvterm_id = $c->req->param("cvterm_id");
323 my $type_id = $c->req->param("type_id");
325 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
327 my $prop_rs = $schema->resultset("Cv::Cvtermprop")->search(
329 'me.cvterm_id' => $cvterm_id,
330 #type_id => $type_id,
331 }, { join => 'type', order_by => 'cvtermprop_id' } );
333 my @propinfo = ();
334 while (my $prop = $prop_rs->next()) {
335 push @propinfo, {cvtermprop_id => $prop->cvtermprop_id, cvterm_id => $prop->cvterm_id, type_id => $prop->type_id(), type_name => $prop->type->name(), value => $prop->value() };
338 $c->stash->{rest} = \@propinfo;
343 sub add_cvtermprop : Path('/cvterm/prop/add') : ActionClass('REST') { }
345 sub add_cvtermprop_POST {
346 my ( $self, $c ) = @_;
347 my $response;
348 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
349 if (!$c->user()) {
350 $c->stash->{rest} = { error => "Log in required for adding stock properties." }; return;
353 if ( any { $_ eq 'curator' || $_ eq 'submitter' || $_ eq 'sequencer' } $c->user->roles() ) {
354 my $req = $c->req;
355 my $cvterm_id = $c->req->param('cvterm_id');
356 my $prop = $c->req->param('prop');
357 my $cv_name = $c->req->param('cv_name') || 'trait_property';
358 $prop =~ s/^\s+|\s+$//g; #trim whitespace from both ends
359 my $prop_type = $c->req->param('prop_type');
361 my $cvterm = $schema->resultset("Cv::Cvterm")->find( { cvterm_id => $cvterm_id } );
363 if ($cvterm && defined($prop) && $prop_type) {
365 try {
366 $cvterm->create_cvtermprops( { $prop_type => $prop }, { cv_name => $cv_name , autocreate => 1 } );
368 my $dbh = $c->dbc->dbh();
369 $c->stash->{rest} = { message => "cvterm_id $cvterm_id and type_id $prop_type have been associated with value $prop. " };
371 } catch {
372 $c->stash->{rest} = { error => "Failed: $_" }
374 } else {
375 $c->stash->{rest} = { error => "Cannot associate prop $prop_type: $prop with cvterm $cvterm_id " };
377 } else {
378 $c->stash->{rest} = { error => 'user does not have a curator/sequencer/submitter account' };
382 sub delete_cvtermprop : Path('/cvterm/prop/delete') : ActionClass('REST') { }
384 sub delete_cvtermprop_GET {
385 my $self = shift;
386 my $c = shift;
387 my $cvtermprop_id = $c->req->param("cvtermprop_id");
388 if (! any { $_ eq 'curator' || $_ eq 'submitter' || $_ eq 'sequencer' } $c->user->roles() ) {
389 $c->stash->{rest} = { error => 'Log in required for deletion of stock properties.' };
390 return;
392 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
393 my $cvtermprop = $schema->resultset("Cv::Cvtermprop")->find( { cvtermprop_id => $cvtermprop_id });
394 if (! $cvtermprop) {
395 $c->stash->{rest} = { error => 'The specified prop does not exist' };
396 return;
398 eval {
399 $cvtermprop->delete();
401 if ($@) {
402 $c->stash->{rest} = { error => "An error occurred during deletion: $@" };
403 return;
405 $c->stash->{rest} = { message => "The cvterm prop was removed from the database." };
408 ####
409 1;##
410 ####