3 SGN::Controller::AJAX::Onto - a REST controller class to provide the
4 backend for the SGN ontology browser
8 Essentially provides four services: roots, children, parents, and
9 cache, that the SGN ontology browser relies on. Output is JSON, as a
10 list of hashes, that has the following keys: accession, has_children,
11 cvterm_name, cvterm_id, and for some functions, parent.
13 The ontologies that should be displayed must be configured in the configuration file (sgn.conf or sgn_local.conf for SGN). Insert a line of the following format into the conf file:
15 C<onto_root_namespaces GO (Gene Ontology), PO (Plant Ontology), SO (Sequence Ontology), PATO (Phenotype and Trait Ontology), SP (Solanaceae Ontology)>
17 where onto_root_namespaces is the conf key, "GO" the two letter code of the ontology (as it appears in the db database table), and in parenthesis is the human readable name of the ontology.
21 Lukas Mueller <lam87@cornell.edu>
27 package SGN
::Controller
::AJAX
::Onto
;
30 use SGN
::Model
::Cvterm
;
31 use CXGN
::Chado
::Cvterm
;
36 use namespace
::autoclean
;
38 BEGIN { extends
'Catalyst::Controller::REST' }
41 default => 'application/json',
43 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
48 Creates a new term in the designated composed trait cv and links it to component terms through cvterm_relationship
52 sub compose_trait
: Path
('/ajax/onto/store_composed_term') Args
(0) {
57 #my @ids = $c->req->param("ids[]");
58 #print STDERR "Ids array for composing in AJAX Onto = @ids\n";
60 my $new_trait_names = decode_json
$c->req->param("new_trait_names");
61 #print STDERR Dumper $new_trait_names;
64 my $onto = CXGN
::Onto
->new( { schema
=> $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado') } );
65 $new_terms = $onto->store_composed_term($new_trait_names);
68 $c->stash->{rest
} = { error
=> "An error occurred saving the new trait details: $@" };
73 foreach (@
$new_terms){
74 $message .= 'Saved new trait <a href="/cvterm/'.$_->[0].'/view">'.$_->[1].'</a><br>';
77 $c->stash->{rest
} = { success
=> $message,
83 =head2 get_trait_from_exact_components
85 searches for and returns (if found) a composed trait that contains the exact components supplied
89 sub get_trait_from_exact_components
: Path
('/ajax/onto/get_trait_from_exact_components') Args
(0) {
93 my @component_ids = $c->req->param("ids[]");
94 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
96 my $trait_id = SGN
::Model
::Cvterm
->get_trait_from_exact_components($schema, \
@component_ids);
98 $c->stash->{rest
} = { error
=> "No exact matches found."};
101 $c->stash->{rest
} = { trait_id
=> $trait_id };
105 =head2 get_trait_from_component_categories
107 searches for and returns traits that contain one of the ids from each id category supplied
111 sub get_traits_from_component_categories
: Path
('/ajax/onto/get_traits_from_component_categories') Args
(0) {
115 my @allowed_composed_cvs = split ',', $c->config->{composable_cvs
};
116 my $composable_cvterm_delimiter = $c->config->{composable_cvterm_delimiter
};
117 my $composable_cvterm_format = $c->config->{composable_cvterm_format
};
118 my @object_ids = $c->req->param("object_ids[]");
119 my @attribute_ids = $c->req->param("attribute_ids[]");
120 my @method_ids = $c->req->param("method_ids[]");
121 my @unit_ids = $c->req->param("unit_ids[]");
122 my @trait_ids = $c->req->param("trait_ids[]");
123 my @tod_ids = $c->req->param("tod_ids[]");
124 my @toy_ids = $c->req->param("toy_ids[]");
125 my @gen_ids = $c->req->param("gen_ids[]");
127 print STDERR
"Obj ids are @object_ids\n Attr ids are @attribute_ids\n Method ids are @method_ids\n unit ids are @unit_ids\n trait ids are @trait_ids\n tod ids are @tod_ids\n toy ids are @toy_ids\n gen ids are @gen_ids\n";
128 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
130 my $traits = SGN
::Model
::Cvterm
->get_traits_from_component_categories($schema, \
@allowed_composed_cvs, $composable_cvterm_delimiter, $composable_cvterm_format, {
131 object
=> \
@object_ids,
132 attribute
=> \
@attribute_ids,
133 method
=> \
@method_ids,
135 trait
=> \
@trait_ids,
142 $c->stash->{rest
} = { error
=> "No matches found."};
145 $c->stash->{rest
} = {
146 existing_traits
=> $traits->{existing_traits
},
147 new_traits
=> $traits->{new_traits
}
155 Public Path: /<ns>/children
157 L<Catalyst::Action::REST> action.
159 Provides a list of child terms, each child term being a hashref (or
160 equivalent) with keys accession, cvterm_name, cvterm_id, has_children,
165 sub children
: Local
: ActionClass
('REST') { }
168 my ( $self, $c ) = @_;
169 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
171 my ($db_name, $accession) = split ":", $c->request->param('node');
173 my $db = $schema->resultset('General::Db')->search({ name
=> uc($db_name) })->first();
174 my $dbxref = $db->find_related('dbxrefs', { accession
=> $accession });
176 my $cvterm = $dbxref->cvterm;
178 my $cvrel_rs = $cvterm->children(); # returns a result set
180 my @response_list = ();
181 while (my $cvrel_row = $cvrel_rs->next()) {
182 my $relationship_node = $cvrel_row->type();
183 my $child_node = $cvrel_row->subject();
185 #only report back children of the same cv namespace
186 # if ($child_node->cv_id() != $cvterm->cv_id()) {
190 my $responsehash = $self->flatten_node($child_node, $relationship_node);
191 push @response_list, $responsehash;
193 @response_list = sort { lc $a->{cvterm_name
} cmp lc $b->{cvterm_name
} } @response_list;
194 $c->stash->{rest
} = \
@response_list;
200 Public Path: /<ns>/parents
202 L<Catalyst::Action::REST> action.
204 Returns a list of hashes with parents information in json, a list of
205 hashrefs with the keys: accession, relationship, has_children, cvterm_id
210 sub parents
: Local
: ActionClass
('REST') { }
215 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
217 my ($db_name, $accession) = split ":", $c->request->param('node');
220 my $db = $schema->resultset('General::Db')->search({ 'upper(name)' => uc($db_name) })->first();
221 if (!$db || !$accession) {
222 #not sure we need here to send an error key, since cache is usually called after parents (? )
223 $response{error
} = "Did not pass a legal ontology term ID! ( $db_name : $accession)";
224 $c->stash->{rest
} = \
%response;
227 $dbxref = $db->find_related('dbxrefs', { accession
=> $accession }) if $db;
229 $response{error
} = "Could not find term $db_name : $accession in the database! Check your input and try again";
230 $c->stash->{rest
} = \
%response;
233 my $cvterm = $dbxref->cvterm;
235 my @response_list = ();
237 my $parents_rs = $cvterm->recursive_parents(); # returns a result set
238 while (my $parent = $parents_rs->next()) {
239 #only report back children of the same cv namespace
240 if ($parent->cv_id() != $cvterm->cv_id()) {
243 my $responsehash = $self->flatten_node($parent, undef);
244 push @response_list, $responsehash;
247 $response{error
} = "Could not find term $db_name : $accession in the database! Check your input and try again. THIS MAY BE AN INTERNAL DATABASE PROBLEM! Please contact sgn-feedback\@sgn.cornell.edu for help.";
248 $c->stash->{rest
} = \
%response;
251 $c->stash->{rest
} = \
@response_list;
265 sub menu
: Local
: ActionClass
('REST') { }
271 my $menudata = $c->config->{onto_root_namespaces
};
273 print STDERR
"MENUDATA: $menudata\n";
274 my @menuitems = split ",", $menudata;
276 my $menu = '<select name="cv_select">';
278 foreach my $mi (@menuitems) {
279 print STDERR
"MENU ITEM: $mi\n";
280 if ($mi =~ /\s*(\w+)?\s*(.*)$/) {
284 $menu .= qq { <option value
="$value">$value $name</option
>\n };
288 $menu .= "</select>\n";
289 $c->stash->{rest
} = [ $menu ];
298 Public Path: /<ns>/roots
300 L<Catalyst::Action::REST> action.
302 Provides the default roots for drawing the ontology browser
306 nodes: optional, a string with namespace definitions, separated by white
307 space (for example, "PO SP SO"). If not provided, will This overrides the standard
308 namespaces provided when called without arguments.
312 sub roots
: Local
: ActionClass
('REST') { }
317 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
319 my $namespace = $c->request->param('nodes');
321 my @response_nodes = ();
322 #my $empty_cvterm = CXGN::Chado::Cvterm->new($c->dbc()->dbh());
323 if (!$namespace) { # should namespaces be db names ? (SO, GO,PO, SP, PATO)
334 @namespaces = split /\s+/, $namespace; #split on whitespace
338 foreach my $ns (@namespaces) {
339 $is_rel = 1 if $ns eq 'OBO_REL';
340 my $q = "SELECT cvterm.cvterm_id FROM cvterm
341 JOIN dbxref USING(dbxref_id) JOIN db USING(db_id)
342 LEFT JOIN cvterm_relationship ON (cvterm.cvterm_id=cvterm_relationship.subject_id)
343 WHERE cvterm_relationship.subject_id IS NULL AND is_obsolete= ? AND is_relationshiptype = ? AND db.name= ? ";
344 my $sth = $schema->storage->dbh->prepare($q);
345 $sth->execute(0,$is_rel,$ns);
346 while (my ($cvterm_id) = $sth->fetchrow_array() ) {
347 my $root = $schema->resultset("Cv::Cvterm")->find( { cvterm_id
=> $cvterm_id } );
351 my @response_list = ();
353 foreach my $r (@roots) {
354 my $hashref = $self->flatten_node($r, undef);
355 push @response_list, $hashref;
357 $c->stash->{rest
}= \
@response_list;
363 Public Path: /<ns>/match
365 L<Catalyst::Action::REST> action.
374 sub match
: Local
: ActionClass
('REST') { }
379 my $db_name = $c->request->param("db_name");
380 my $term_name = $c->request->param("term_name");
382 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
383 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
385 JOIN dbxref USING (db_id ) JOIN cvterm USING (dbxref_id)
386 JOIN cv USING (cv_id )
387 LEFT JOIN cvtermsynonym USING (cvterm_id )
388 WHERE db.name = ? AND (cvterm.name ilike ? OR cvtermsynonym.synonym ilike ? OR cvterm.definition ilike ?) AND cvterm.is_obsolete = 0
389 GROUP BY cvterm.cvterm_id,cv.name, cvterm.name, dbxref.accession, db.name ";
390 my $sth= $schema->storage->dbh->prepare($query);
391 $sth->execute($db_name, "\%$term_name\%", "\%$term_name\%", "\%$term_name\%");
393 while (my $hashref = $sth->fetchrow_hashref ) {
394 push @response_list, $hashref;
396 $c->stash->{rest
} = \
@response_list;
401 Public Path: /<ns>/cache
403 L<Catalyst::Action::REST> action.
405 Provides a list of parents and their direct children in a denormalized
406 list. The parameter node is used to determine all the children that
407 need to be cached for the parentage view to render fast (without
408 having to call the children ajax function).
412 sub cache
: Local
: ActionClass
('REST') { }
418 $self->{duplicates
} = {};
419 $self->{cache_list
} = [];
421 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
423 my ($db_name, $accession) = split ":", $c->request->param('node');
424 if (!$db_name || !$accession) {
425 $response{error
} = "Looks like you passed an illegal ontology term ID ! ($db_name : $accession) Please try again.";
426 $c->stash->{rest
} = \
%response;
430 my $db = $schema->resultset('General::Db')->search({ name
=> $db_name })->first();
431 my $dbxref = $db->find_related('dbxrefs', { accession
=> $accession });
433 $response{error
} = "Did not find ontology term $db_name : $accession in the database. Please try again. If you think this term should exist please contact sgn-feedback\@sgn.cornell.edu";
434 $c->stash->{rest
} = \
%response;
438 my $cvterm = $dbxref->cvterm;
440 $response{error
} = "Did not find ontology term $db_name : $accession in the database. This may be an internal database issue. Please contact sgn-feedback\@sgn.cornell.edu and we will fic this error ASAP";
441 $c->stash->{rest
} = \
%response;
444 my $parents_rs = $cvterm->recursive_parents(); # returns a result set
445 if (!$parents_rs->next) {
446 $response{error
} = "did not find recursive parents for cvterm " . $cvterm->name;
447 $c->stash->{rest
} = \
%response;
451 # $self->add_cache_list(undef, $cvterm, $cvterm->type());
452 while (my $p = $parents_rs->next()) {
453 my $children_rs = $p->children();
454 while (my $rel_rs = $children_rs->next()) { # returns a list of cvterm rows
455 my $child = $rel_rs->subject();
456 $self->add_cache_list($p, $child, $rel_rs->type());
459 $c->stash->{rest
} = $self->{cache_list
};
463 =head1 PRIVATE ACTIONS
465 =head2 add_cache_list
469 Adds an entry to the cache list.
471 Argsuments: 3 cvterm row objects: Parent, child, relationship
475 sub add_cache_list
:Private
{
477 my $parent = shift; # object
478 my $child = shift; # object
479 my $relationship = shift; #object
481 my $unique_hashkey = $parent->cvterm_id()." ".$child->cvterm_id();
482 if (exists($self->{duplicates
}->{$unique_hashkey})) {
486 $self->{duplicates
}->{$unique_hashkey}++;
488 my $hashref = $self->flatten_node($child, $relationship);
489 ###$hashref->{parent} = $parent->get_full_accession();
491 my $dbxref = $parent->dbxref();
492 my $parent_accession = $dbxref->db()->name().":".$dbxref->accession();
493 $hashref->{parent
} = $parent_accession;
494 ##print STDERR "Adding to cache list: parent=$parent. child=$child\n";
495 push @
{$self->{cache_list
}}, $hashref;
499 ### used for cvterm resultset
502 my $node_row = shift;
505 my $has_children = 0;
506 if ($node_row->children()->first()) {
512 $rel_name = $rel_row->name();
515 my $dbxref = $node_row->dbxref();
518 { accession
=> $dbxref->db->name().":".$dbxref->accession,
519 cvterm_name
=> $node_row->name(),
520 cvterm_id
=> $node_row->cvterm_id(),
521 has_children
=> $has_children,
522 relationship
=> $rel_name,