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' },
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 store_ontology_identifier
85 Creates a ontology identifier by adding an entry in the DB, cv, and cvterm tables.
89 sub store_ontology_identifier
: Path
('/ajax/onto/store_ontology_identifier') Args
(0) {
92 #print STDERR Dumper $c->req->params();
97 my $session_id = $c->req->param("sgn_session_id");
100 my $dbh = $c->dbc->dbh;
101 my @user_info = CXGN
::Login
->new($dbh)->query_from_cookie($session_id);
103 $c->stash->{rest
} = {error
=>'You must be logged in to create ontology!'};
106 $user_id = $user_info[0];
107 $user_role = $user_info[1];
108 my $p = CXGN
::People
::Person
->new($dbh, $user_id);
109 $user_name = $p->get_username;
112 $c->stash->{rest
} = {error
=>'You must be logged in to create ontology!'};
115 $user_id = $c->user()->get_object()->get_sp_person_id();
116 $user_name = $c->user()->get_object()->get_username();
117 $user_role = $c->user->get_object->get_user_type();
120 if ($user_role ne 'curator') {
121 $c->stash->{rest
} = {error
=> "You have insufficient privileges to add ontology." };
125 my $ontology_name = $c->req->param("ontology_name");
126 my $ontology_description = $c->req->param("ontology_description");
127 my $ontology_identifier = $c->req->param("ontology_identifier");
128 my $ontology_type = $c->req->param("ontology_type");
131 if ($c->config->{allow_observation_variable_submission_interface
}) {
132 my $onto = CXGN
::Onto
->new( { schema
=> $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado') } );
133 my $return = $onto->store_ontology_identifier(
135 $ontology_description,
136 $ontology_identifier,
139 if ($return->{error
}) {
140 $finish{error
} = $return->{error
};
141 } elsif ($return->{success
}) {
142 $finish{success
} = 'Saved new ontology <a href="/cvterm/'.$return->{new_term
}->[0].'/view">'.$return->{new_term
}->[1].'</a><br>';
144 $finish{error
} = 'Something went wrong!';
147 $finish{error
} = 'On this database it is not allowed for users to add their own ontology! Please contact us!';
149 $c->stash->{rest
} = \
%finish;
152 =head2 store_trait_method_scale_observation_variable
154 Creates a new term in the designated observation variable cv and links it to component trait, method, and scale terms through cvterm_relationship. will create trait, method, and scale terms in their own ontologies if they need to be.
158 sub store_trait_method_scale_observation_variable
: Path
('/ajax/onto/store_trait_method_scale_observation_variable') Args
(0) {
161 #print STDERR Dumper $c->req->params();
166 my $session_id = $c->req->param("sgn_session_id");
169 my $dbh = $c->dbc->dbh;
170 my @user_info = CXGN
::Login
->new($dbh)->query_from_cookie($session_id);
172 $c->stash->{rest
} = {error
=>'You must be logged in to create observation variables!'};
175 $user_id = $user_info[0];
176 $user_role = $user_info[1];
177 my $p = CXGN
::People
::Person
->new($dbh, $user_id);
178 $user_name = $p->get_username;
181 $c->stash->{rest
} = {error
=>'You must be logged in to create observation variables!'};
184 $user_id = $c->user()->get_object()->get_sp_person_id();
185 $user_name = $c->user()->get_object()->get_username();
186 $user_role = $c->user->get_object->get_user_type();
189 if ($user_role ne 'curator') {
190 $c->stash->{rest
} = {error
=> "You have insufficient privileges to add observation variables." };
194 my $selected_observation_variable_db_id = $c->req->param("selected_observation_variable_db_id");
195 my $new_observation_variable_name = $c->req->param("new_observation_variable_name");
196 my $new_observation_variable_definition = $c->req->param("new_observation_variable_definition");
197 my $selected_trait_db_id = $c->req->param("selected_trait_db_id");
198 my $selected_trait_cvterm_id = $c->req->param("selected_trait_cvterm_id");
199 my $new_trait_name = $c->req->param("new_trait_name");
200 my $new_trait_definition = $c->req->param("new_trait_definition");
201 my $selected_method_db_id = $c->req->param("selected_method_db_id");
202 my $selected_method_cvterm_id = $c->req->param("selected_method_cvterm_id");
203 my $new_method_name = $c->req->param("new_method_name");
204 my $new_method_definition = $c->req->param("new_method_definition");
205 my $selected_scale_db_id = $c->req->param("selected_scale_db_id");
206 my $selected_scale_cvterm_id = $c->req->param("selected_scale_cvterm_id");
207 my $new_scale_name = $c->req->param("new_scale_name");
208 my $new_scale_definition = $c->req->param("new_scale_definition");
209 my $new_scale_format = $c->req->param("new_scale_format");
210 my $new_scale_minumum = $c->req->param("new_scale_minimum");
211 my $new_scale_maximum = $c->req->param("new_scale_maximum");
212 my $new_scale_default = $c->req->param("new_scale_default");
213 my $new_scale_categories = $c->req->param("new_scale_categories");
216 if ($c->config->{allow_observation_variable_submission_interface
}) {
217 my $onto = CXGN
::Onto
->new( { schema
=> $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado') } );
218 my $return = $onto->store_observation_variable_trait_method_scale(
219 $selected_observation_variable_db_id,
220 $new_observation_variable_name,
221 $new_observation_variable_definition,
222 $selected_trait_db_id,
223 $selected_trait_cvterm_id,
225 $new_trait_definition,
226 $selected_method_db_id,
227 $selected_method_cvterm_id,
229 $new_method_definition,
230 $selected_scale_db_id,
231 $selected_scale_cvterm_id,
233 $new_scale_definition,
238 $new_scale_categories
240 if ($return->{error
}) {
241 $finish{error
} = $return->{error
};
242 } elsif ($return->{success
}) {
243 $finish{success
} = 'Saved new observation variable <a href="/cvterm/'.$return->{new_term
}->[0].'/view">'.$return->{new_term
}->[1].'</a><br>';
245 $finish{error
} = 'Something went wrong!';
248 $finish{error
} = 'On this database it is not allowed for users to add their own observation variables! Please contact us!';
250 $c->stash->{rest
} = \
%finish;
253 =head2 get_trait_from_exact_components
255 searches for and returns (if found) a composed trait that contains the exact components supplied
259 sub get_trait_from_exact_components
: Path
('/ajax/onto/get_trait_from_exact_components') Args
(0) {
263 my @component_ids = $c->req->param("ids[]");
264 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
266 my $trait_id = SGN
::Model
::Cvterm
->get_trait_from_exact_components($schema, \
@component_ids);
268 $c->stash->{rest
} = { error
=> "No exact matches found."};
271 $c->stash->{rest
} = { trait_id
=> $trait_id };
275 =head2 get_trait_from_component_categories
277 searches for and returns traits that contain one of the ids from each id category supplied
281 sub get_traits_from_component_categories
: Path
('/ajax/onto/get_traits_from_component_categories') Args
(0) {
285 my @allowed_composed_cvs = split ',', $c->config->{composable_cvs
};
286 my $composable_cvterm_delimiter = $c->config->{composable_cvterm_delimiter
};
287 my $composable_cvterm_format = $c->config->{composable_cvterm_format
};
288 my @object_ids = $c->req->param("object_ids[]");
289 my @attribute_ids = $c->req->param("attribute_ids[]");
290 my @method_ids = $c->req->param("method_ids[]");
291 my @unit_ids = $c->req->param("unit_ids[]");
292 my @trait_ids = $c->req->param("trait_ids[]");
293 my @tod_ids = $c->req->param("tod_ids[]");
294 my @toy_ids = $c->req->param("toy_ids[]");
295 my @gen_ids = $c->req->param("gen_ids[]");
296 my @evt_ids = $c->req->param("evt_ids[]");
298 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 evt ids are @evt_ids\n";
299 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
301 my $traits = SGN
::Model
::Cvterm
->get_traits_from_component_categories($schema, \
@allowed_composed_cvs, $composable_cvterm_delimiter, $composable_cvterm_format, {
302 object
=> \
@object_ids,
303 attribute
=> \
@attribute_ids,
304 method
=> \
@method_ids,
306 trait
=> \
@trait_ids,
314 $c->stash->{rest
} = { error
=> "No matches found."};
317 $c->stash->{rest
} = {
318 existing_traits
=> $traits->{existing_traits
},
319 new_traits
=> $traits->{new_traits
}
327 Public Path: /<ns>/children
329 L<Catalyst::Action::REST> action.
331 Provides a list of child terms, each child term being a hashref (or
332 equivalent) with keys accession, cvterm_name, cvterm_id, has_children,
337 sub children
: Local
: ActionClass
('REST') { }
340 my ( $self, $c ) = @_;
341 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
343 my ($db_name, $accession) = split ":", $c->request->param('node');
345 my $db = $schema->resultset('General::Db')->search({ name
=> uc($db_name) })->first();
346 my $dbxref = $db->find_related('dbxrefs', { accession
=> $accession });
348 my $cvterm = $dbxref->cvterm;
350 my $cvrel_rs = $cvterm->children(); # returns a result set
352 my @response_list = ();
353 while (my $cvrel_row = $cvrel_rs->next()) {
354 my $relationship_node = $cvrel_row->type();
355 my $child_node = $cvrel_row->subject();
357 #only report back children of the same cv namespace
358 # if ($child_node->cv_id() != $cvterm->cv_id()) {
362 my $responsehash = $self->flatten_node($child_node, $relationship_node);
363 push @response_list, $responsehash;
365 @response_list = sort { lc $a->{cvterm_name
} cmp lc $b->{cvterm_name
} } @response_list;
366 $c->stash->{rest
} = \
@response_list;
372 Public Path: /<ns>/parents
374 L<Catalyst::Action::REST> action.
376 Returns a list of hashes with parents information in json, a list of
377 hashrefs with the keys: accession, relationship, has_children, cvterm_id
382 sub parents
: Local
: ActionClass
('REST') { }
387 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
389 my ($db_name, $accession) = split ":", $c->request->param('node');
392 my $db = $schema->resultset('General::Db')->search(
393 { 'upper(me.name)' => uc($db_name),
394 'cvterm.name' => {'!=', undef },
395 'dbxrefs.accession' => $accession
397 {join => { 'dbxrefs' => 'cvterm' }
401 if (!$db || !$accession) {
402 #not sure we need here to send an error key, since cache is usually called after parents (? )
403 $response{error
} = "Did not pass a legal ontology term ID! ( $db_name : $accession)";
404 $c->stash->{rest
} = \
%response;
406 } elsif ( $db->count > 1 ) {
407 $response{error
} = "Found more than one db row for db.name $db_name : check your database";
408 $c->stash->{rest
} = \
%response;
411 $db_id=$db->next->db_id;
412 my $sql = 'IS NOT NULL ';
413 my $dbxref_rs = $schema->resultset('General::Dbxref')->search(
414 { 'me.accession' => $accession,
416 'cvterm.cvterm_id' => \
$sql
419 { join => 'cvterm' },
422 if ($dbxref_rs->count >1 ) {
423 while (my $d = $dbxref_rs->next() ) { print STDERR
"DBXREF = " . $d->dbxref_id . " CVTERM = " . $d->cvterm->cvterm_id . "NAME = " . $d->cvterm->name . " \n\n" ; }
424 $response{error
} = "Found more than one dbxref row for accession $accession : check your database";
425 $c->stash->{rest
} = \
%response;
428 $dbxref = $dbxref_rs->next();
430 #print STDERR "***Onto.pm: My db_name = $db_name , accession = $accession, db = " . $db_name . "db id = " . $db_id . " cvterm = " . $dbxref->cvterm->cvterm_id . "\n\n";
432 $response{error
} = "Could not find term $db_name : $accession in the database! Check your input and try again";
433 $c->stash->{rest
} = \
%response;
436 my $cvterm = $dbxref->cvterm;
438 my @response_list = ();
440 my $parents_rs = $cvterm->recursive_parents(); # returns a result set
441 while (my $parent = $parents_rs->next()) {
442 #only report back children of the same cv namespace
443 if ($parent->cv_id() != $cvterm->cv_id()) {
446 my $responsehash = $self->flatten_node($parent, undef);
447 push @response_list, $responsehash;
450 $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.";
451 $c->stash->{rest
} = \
%response;
454 $c->stash->{rest
} = \
@response_list;
468 sub menu
: Local
: ActionClass
('REST') { }
474 my $menudata = $c->config->{onto_root_namespaces
};
476 print STDERR
"MENUDATA: $menudata\n";
477 my @menuitems = split ",", $menudata;
479 my $menu = '<select name="cv_select">';
481 foreach my $mi (@menuitems) {
482 print STDERR
"MENU ITEM: $mi\n";
483 if ($mi =~ /\s*(\w+)?\s*(.*)$/) {
487 $menu .= qq { <option value
="$value">$value $name</option
>\n };
491 $menu .= "</select>\n";
492 $c->stash->{rest
} = [ $menu ];
501 Public Path: /<ns>/roots
503 L<Catalyst::Action::REST> action.
505 Provides the default roots for drawing the ontology browser
509 nodes: optional, a string with namespace definitions, separated by white
510 space (for example, "PO SP SO"). If not provided, will This overrides the standard
511 namespaces provided when called without arguments.
515 sub roots
: Local
: ActionClass
('REST') { }
520 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
522 my $namespace = $c->request->param('nodes');
524 my @response_nodes = ();
525 #my $empty_cvterm = CXGN::Chado::Cvterm->new($c->dbc()->dbh());
526 if (!$namespace) { # should namespaces be db names ? (SO, GO,PO, SP, PATO)
537 @namespaces = split /\s+/, $namespace; #split on whitespace
541 foreach my $ns (@namespaces) {
542 $is_rel = 1 if $ns eq 'OBO_REL';
543 my $q = "SELECT cvterm.cvterm_id FROM cvterm
544 JOIN dbxref USING(dbxref_id) JOIN db USING(db_id)
545 LEFT JOIN cvterm_relationship ON (cvterm.cvterm_id=cvterm_relationship.subject_id)
546 WHERE cvterm_relationship.subject_id IS NULL AND is_obsolete= ? AND is_relationshiptype = ? AND db.name= ? ";
547 my $sth = $schema->storage->dbh->prepare($q);
548 $sth->execute(0,$is_rel,$ns);
549 while (my ($cvterm_id) = $sth->fetchrow_array() ) {
550 my $root = $schema->resultset("Cv::Cvterm")->find( { cvterm_id
=> $cvterm_id } );
554 my @response_list = ();
556 foreach my $r (@roots) {
557 my $hashref = $self->flatten_node($r, undef);
558 push @response_list, $hashref;
560 $c->stash->{rest
}= \
@response_list;
566 Public Path: /<ns>/match
568 L<Catalyst::Action::REST> action.
577 sub match
: Local
: ActionClass
('REST') { }
582 my $db_name = $c->request->param("db_name");
583 my $term_name = $c->request->param("term_name");
585 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
586 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
588 JOIN dbxref USING (db_id ) JOIN cvterm USING (dbxref_id)
589 JOIN cv USING (cv_id )
590 LEFT JOIN cvtermsynonym USING (cvterm_id )
591 WHERE db.name = ? AND (cvterm.name ilike ? OR cvtermsynonym.synonym ilike ? OR cvterm.definition ilike ?) AND cvterm.is_obsolete = 0
592 GROUP BY cvterm.cvterm_id,cv.name, cvterm.name, dbxref.accession, db.name ";
593 my $sth= $schema->storage->dbh->prepare($query);
594 $sth->execute($db_name, "\%$term_name\%", "\%$term_name\%", "\%$term_name\%");
596 while (my $hashref = $sth->fetchrow_hashref ) {
597 push @response_list, $hashref;
599 $c->stash->{rest
} = \
@response_list;
604 Public Path: /<ns>/cache
606 L<Catalyst::Action::REST> action.
608 Provides a list of parents and their direct children in a denormalized
609 list. The parameter node is used to determine all the children that
610 need to be cached for the parentage view to render fast (without
611 having to call the children ajax function).
615 sub cache
: Local
: ActionClass
('REST') { }
621 $self->{duplicates
} = {};
622 $self->{cache_list
} = [];
624 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
626 my ($db_name, $accession) = split ":", $c->request->param('node');
627 if (!$db_name || !$accession) {
628 $response{error
} = "Looks like you passed an illegal ontology term ID ! ($db_name : $accession) Please try again.";
629 $c->stash->{rest
} = \
%response;
634 my $db = $schema->resultset('General::Db')->search(
635 { 'upper(me.name)' => uc($db_name),
636 'cvterm.name' => {'!=', undef },
637 'dbxrefs.accession' => $accession
639 {join => { 'dbxrefs' => 'cvterm' }
642 my ($db_id, $dbxref);
643 $db_id=$db->next->db_id;
644 my $sql = 'IS NOT NULL ';
645 my $dbxref_rs = $schema->resultset('General::Dbxref')->search(
646 { 'me.accession' => $accession,
648 'cvterm.cvterm_id' => \
$sql
651 { join => 'cvterm' },
654 if ($dbxref_rs->count >1 ) {
655 while (my $d = $dbxref_rs->next() ) { print STDERR
"DBXREF = " . $d->dbxref_id . " CVTERM = " . $d->cvterm->cvterm_id . "NAME = " . $d->cvterm->name . " \n\n" ; }
656 $response{error
} = "Found more than one dbxref row for accession $accession : check your database";
657 $c->stash->{rest
} = \
%response;
660 $dbxref = $dbxref_rs->next();
663 $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";
664 $c->stash->{rest
} = \
%response;
667 ######################
668 my $cvterm = $dbxref->cvterm;
670 $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 fix this error ASAP";
671 $c->stash->{rest
} = \
%response;
674 my $parents_rs = $cvterm->recursive_parents(); # returns a result set
675 if (!$parents_rs->next) {
676 $response{error
} = "did not find recursive parents for cvterm " . $cvterm->name;
677 $c->stash->{rest
} = \
%response;
681 # $self->add_cache_list(undef, $cvterm, $cvterm->type());
682 while (my $p = $parents_rs->next()) {
683 my $children_rs = $p->children();
684 while (my $rel_rs = $children_rs->next()) { # returns a list of cvterm rows
685 my $child = $rel_rs->subject();
686 $self->add_cache_list($p, $child, $rel_rs->type());
689 $c->stash->{rest
} = $self->{cache_list
};
693 =head1 PRIVATE ACTIONS
695 =head2 add_cache_list
699 Adds an entry to the cache list.
701 Argsuments: 3 cvterm row objects: Parent, child, relationship
705 sub add_cache_list
:Private
{
707 my $parent = shift; # object
708 my $child = shift; # object
709 my $relationship = shift; #object
711 my $unique_hashkey = $parent->cvterm_id()." ".$child->cvterm_id();
712 if (exists($self->{duplicates
}->{$unique_hashkey})) {
716 $self->{duplicates
}->{$unique_hashkey}++;
718 my $hashref = $self->flatten_node($child, $relationship);
719 ###$hashref->{parent} = $parent->get_full_accession();
721 my $dbxref = $parent->dbxref();
722 my $parent_accession = $dbxref->db()->name().":".$dbxref->accession();
723 $hashref->{parent
} = $parent_accession;
724 ##print STDERR "Adding to cache list: parent=$parent. child=$child\n";
725 push @
{$self->{cache_list
}}, $hashref;
729 ### used for cvterm resultset
732 my $node_row = shift;
735 my $has_children = 0;
736 if ($node_row->children()->first()) {
742 $rel_name = $rel_row->name();
745 my $dbxref = $node_row->dbxref();
748 { accession
=> $dbxref->db->name().":".$dbxref->accession,
749 cvterm_name
=> $node_row->name(),
750 cvterm_id
=> $node_row->cvterm_id(),
751 has_children
=> $has_children,
752 relationship
=> $rel_name,