fixed recursive_children cvterm function, and added tests for parents and children
[cxgn-corelibs.git] / lib / CXGN / Chado / Cvterm.pm
bloba4e6d3c3b51ab80a3b5f272c7c6611627b5d27e8
2 =head1 NAME
4 CXGN::Chado::Cvterm - a class to handle controlled vocabulary terms.
6 =head1 SYNOPSIS
8 =head1 AUTHOR
10 Naama Menda <nm249@cornell.edu>
11 Lukas Mueller <lam87@cornell.edu> (added implementation of Bio::Ontology::TermI)
12 (added map_to_slim function [March 3, 2008])
14 =cut
16 package CXGN::Chado::Cvterm;
18 use Data::Dumper;
19 use CXGN::DB::Connection;
20 use CXGN::Chado::Dbxref;
21 use CXGN::Phenome::Population;
22 use CXGN::Chado::Cvterm::CvtermRanking;
23 use CXGN::Phenome::Locus;
24 use CXGN::Phenome::Individual;
25 use CXGN::Phenome::Qtl::Tools;
26 use List::MoreUtils qw /uniq/;
27 use strict;
29 use base qw / CXGN::DB::Object CXGN::Chado::Cvterm::CvtermRanking / ;
32 =head1 IMPLEMENTATION OF THE Bio::Ontology::TermI INTERFACE
34 =head2 identifier
36 Usage: $t->set_identifier("0000001");
37 Desc: this identifier function maps to the accession.
38 it is a synonym for set_accession and get_accession.
39 For the go accession GO:000222, this would have to
40 be set to 000222, for example.
41 Property: the accession of the term.
42 Side Effects:
43 Example:
45 =cut
47 sub identifier {
48 my $self = shift;
49 my $identifier = shift;
50 $self->set_accession($identifier) if $identifier;
51 return $self->get_accession();
55 =head2 name
57 Usage: $t->name($name);
58 Desc: a synonym for the set_cvterm_name/get_cvterm_name accessors
59 Ret:
60 Args:
61 Side Effects:
62 Example:
64 =cut
66 sub name {
67 my $self = shift;
68 my $name = shift;
69 $self->set_cvterm_name($name) if $name;
70 return $self->get_cvterm_name();
73 =head2 definition
75 Usage: $t->definition($definition);
76 Desc: a synonym for the set_definition/get_definition
77 accessors
78 Ret:
79 Args:
80 Side Effects:
81 Example:
83 =cut
85 sub definition {
86 my $self = shift;
87 my $definition = shift;
88 $self->set_definition($definition) if defined($definition);
89 return $self->get_definition();
92 =head2 ontology
94 Usage: $t->ontology( CXGN::Chado::Ontology->new_with_name($dbh, "gene_ontology");
95 Desc: setter/getter for the ontology object this terms belongs to
96 Property: a CXGN::Chado::Ontology object.
97 Side Effects:
98 Example:
100 =cut
102 sub ontology {
103 my $self = shift;
104 my $ontology = shift;
105 if ($ontology) {
106 $self->set_cv_id($ontology->identifier());
108 else {
109 return CXGN::Chado::Ontology->new($self->get_dbh(), $self->get_cv_id());
114 =head2 version
116 Usage: my $v = $t->version($version)
117 Desc: synonym for get/set_version . See L<Bio::Ontology::OntologyI>.
118 Ret: $self->get_version()
119 Args: $version
120 Side Effects:
121 Example:
123 =cut
125 sub version {
126 my $self = shift;
127 my $version = shift;
128 $self->set_version($version) if defined($version);
129 return $self->get_version();
132 =head2 comment
134 Usage: $cvterm->comment("This is a useful term!");
135 my $comment = $cvterm->comment();
136 Desc: setter/getter for the comment
137 Args/Ret: the comment, a string.
138 Side Effects: accesses the database and retrieves/stores comments
139 on the fly. Thus, the cvterm needs to be stored in the
140 database and have a legal cvterm_id. The function emits
141 a warning and does nothing if that is not the case.
142 Example:
144 =cut
146 sub comment {
147 my $self = shift;
148 my $comment = shift;
149 my $type_id=CXGN::Chado::Cvterm::get_cvterm_by_name($self->get_dbh(), "comment")->get_cvterm_id();
150 if (!$type_id) {
151 $self->d("WARNING. Cvterm has not yet been stored. Skipping the comment update.\n");
152 return undef;
154 if ($comment) {
155 # check if there is already a comment associated with this term.
156 my $query = "SELECT cvtermprop_id from cvtermprop WHERE cvterm_id=? and value=?";
157 my $sth = $self->get_dbh()->prepare($query);
158 $sth->execute($self->get_cvterm_id(), $comment);
159 my %props; #hash of arrays for cvtermprop_id => comment pairs
160 my ($cvtermprop_id) = $sth->fetchrow_array();
161 #my @comments=();
162 #while (my ($value) = $sth->fetchrow_array()) { push @comments , $value; }
163 # if yes, update it
164 #if ($cvtermprop_id) {
165 # my $update = "UPDATE cvtermprop SET value=? WHERE cvtermprop_id=?";
166 # my $update_h = $self->get_dbh()->prepare($update);
167 # $update_h->execute($comment, $cvtermprop_id);
169 # if does not exist- insert a new one.
170 if (!$cvtermprop_id) {
171 my $insert = "INSERT INTO cvtermprop (cvterm_id, type_id, value, rank)
172 VALUES (?, ?, ?, ?)";
173 my $insert_h = $self->get_dbh()->prepare($insert);
174 $insert_h->execute($self->get_cvterm_id(),
175 $type_id,
176 $comment,
181 else {
182 # query the comment out of the database
183 my $query = "SELECT value FROM cvtermprop WHERE cvterm_id=?";
184 my $sth = $self->get_dbh()->prepare($query);
185 $sth->execute($self->get_cvterm_id());
186 my ($comment) = $sth->fetchrow_array();
187 return $comment;
191 =head2 get_dblinks
193 Usage:
194 Desc:
195 Ret:
196 Args:
197 Side Effects:
198 Example:
200 =cut
202 sub get_dblinks {
203 my $self = shift;
204 return $self->get_dbxref_id();
208 =head1 ORIGINAL CXGN::Chado::Cvterm functions
210 =cut
214 =head2 get_roots
216 Usage: my @roots = CXGN::Chado::Cvterm::get_roots($dbh, "PO");
217 Desc: a static function that returns a list of root
218 terms for a given namespace such as "GO" or "PO"
220 Ret: a list of cvterm objects that are roots of ontologies
221 Args: $dbh and a namespace
222 Side Effects:
223 Example:
225 =cut
227 sub get_roots {
228 my $dbh = shift;
229 my $namespace = shift;
231 my $query = "select cvterm.cvterm_id, cvterm.name from cv join cvterm using (cv_id) join dbxref using(dbxref_id) join db using(db_id) left join cvterm_relationship on (cvterm.cvterm_id=cvterm_relationship.subject_id) where cvterm_relationship.subject_id is null and is_obsolete=0 and is_relationshiptype =0 and db.name=? order by cvterm.name asc";
232 my $sth = $dbh->prepare($query);
233 $sth->execute($namespace);
234 my @roots = ();
235 while (my ($cvterm_id) = $sth->fetchrow_array()) {
236 push @roots, CXGN::Chado::Cvterm->new($dbh, $cvterm_id);
238 return @roots;
243 =head2 get_namespaces
245 Usage: my @namespaces = CXGN::Chado::Cvterm::get_namespaces($dbh)
246 Desc: currently provides a list of supported namespaces that
247 are hardcoded. Needs to be refactored somehow to
248 get the useful namespaces from the database (public.db table).
249 Ret:
250 Args:
251 Side Effects:
252 Example:
254 =cut
256 sub get_namespaces {
257 my $dbh = shift;
259 return ("PO", "GO", "SP", "SO", "PATO");
266 =head2 new
268 Usage: my $cvterm = CXGN::Chado::Cvterm->new($dbh, $cvterm_id);
269 Desc: an object for handling controlled vocabullary term objects
270 Ret:
271 Args:$dbh a database handle
272 $cvterm_id = an id of a controlled vocabulary term (from chado cvterm table)
273 Side Effects:
274 Example:
276 =cut
279 sub new {
280 my $class = shift;
281 my $dbh=shift;
282 my $cvterm_id = shift; #id of the cvterm
284 my $self= $class->SUPER::new($dbh);
286 if ($cvterm_id) {
287 $self->set_cvterm_id($cvterm_id);
288 $self->fetch();
289 if (!$self->get_cvterm_id()) { # the cvterm supplied was not one that exists
290 return undef;
294 return $self;
297 =head2 new_with_term_name
299 Usage: my $isa = CXGN::Chado::Cvterm->new_with_term_name($dbh, "isa", $cv_id);
300 Desc: useful for getting the term objects for relationship terms.
301 limited usefulness for any other type of term.
302 Ret: a term object
303 Args: a database handle, a term name and a cv_id
304 Side Effects:
305 Example:
307 =cut
309 sub new_with_term_name {
310 my $class = shift;
311 my $dbh = shift;
312 my $name = shift;
313 my $cv_id = shift;
315 if (!$name && !$cv_id) { die "[Cvterm.pm] new_with_term_name: Need name and cv_id."; }
317 my $query = "SELECT cvterm_id from cvterm where name ilike ? and cv_id=?";
318 my $sth = $dbh->prepare($query);
319 $sth->execute($name, $cv_id);
320 my ($cvterm_id) = $sth->fetchrow_array();
322 my $self = CXGN::Chado::Cvterm->new($dbh, $cvterm_id);
323 return $self;
326 =head2 new_with_accession
328 Usage: my $t = CXGN::Chado::Cvterm->new_with_accession($dbh, "InterPro:IPR000001", $cv_id);
329 Desc: an alternate constructor using the accession to define the term.
330 Ret:
331 Args: a database handle [CXGN::DB::Connection], an accession [string], cv_id (optional. Can be useful if you think the accession might exist in another other cv)
332 Side Effects:
333 Example:
335 =cut
337 sub new_with_accession {
338 my $class = shift;
339 my $dbh = shift;
340 my $accession = shift;
341 my $cv_id = shift;
343 my ($name_space, $id) = split "\:", $accession;
345 if ($accession =~ m/^IPR*/ ) {
346 $name_space = 'InterPro';
347 $id= $accession;
350 my $query = "SELECT cvterm_id FROM cvterm join dbxref using(dbxref_id) JOIN db USING (db_id) WHERE db.name=? AND dbxref.accession ilike ?";
351 $query .= " AND cv_id = $cv_id" if $cv_id;
353 my $sth = $dbh->prepare($query);
354 $sth->execute($name_space, $id);
356 my ($cvterm_id) = $sth->fetchrow_array();
358 my $self = $class->new($dbh, $cvterm_id);
359 return $self;
363 sub fetch {
364 my $self=shift;
366 my $cvterm_query = $self->get_dbh()->prepare("SELECT cvterm.cvterm_id, cv.cv_id, cv.name, cvterm.name, cvterm.definition, cvterm.dbxref_id, cvterm.is_obsolete, cvterm.is_relationshiptype, dbxref.accession, db.name FROM public.cvterm join public.cv using(cv_id) join public.dbxref using(dbxref_id) join public.db using (db_id) WHERE cvterm_id=? ");
368 $cvterm_query->execute( $self->get_cvterm_id() );
371 my ($cvterm_id, $cv_id, $cv_name, $cvterm_name, $definition, $dbxref_id, $obsolete,$is_rel,$accession, $db_name)=$cvterm_query->fetchrow_array();
372 $self->set_cvterm_id($cvterm_id);
373 $self->set_cv_id($cv_id);
374 $self->set_cv_name($cv_name);
375 $self->set_cvterm_name($cvterm_name);
376 $self->set_definition($definition);
377 $self->set_dbxref_id($dbxref_id);
378 $self->set_obsolete($obsolete);
379 $self->set_is_relationshiptype($is_rel);
380 $self->set_accession($accession);
381 $self->set_db_name($db_name);
384 =head2 store
386 Usage: $self->store()
387 Desc: store a new cvterm. Update if cvterm_id exists
388 Ret: cvterm_id
389 Args: none
390 Side Effects: store a new dbxref for the cvterm if does not exists already
391 Example:
393 =cut
395 sub store {
396 my $self = shift;
397 my $cvterm_id=$self->get_cvterm_id();
399 if ($cvterm_id) {
400 #check if exists:
401 my $existing_cvterm_id=$self->cvterm_exists();
402 if ($existing_cvterm_id) {
403 $self->d( "Cvterm.pm found existing cvterm_id $existing_cvterm_id.. can't update term " .$self->get_cvterm_id() . "! \n");
404 }else {
405 # update
406 my $query = "UPDATE cvterm set cv_id=?, name=?, dbxref_id=?, definition=?, is_obsolete=? WHERE cvterm_id=?";
407 my $sth = $self->get_dbh()->prepare($query);
408 $sth->execute($self->get_cv_id(), $self->get_cvterm_name(), $self->get_dbxref_id(), $self->get_definition(), $self->get_obsolete(), $self->get_cvterm_id());
410 }else {
411 if (!$self->get_dbxref_id()) {
412 if (!$self->get_accession()) { die "Need an accession for a CV term!"; }
413 my $dbxref = CXGN::Chado::Dbxref->new($self->get_dbh());
414 $dbxref->set_accession($self->get_accession());
415 $dbxref->set_version($self->get_version());
416 $dbxref->set_description($self->definition());
417 my $db_name = $self->get_db_name();
418 if ($db_name) { $dbxref->set_db_name($db_name); }
419 else { die "Need a DB name to store cvterm object.\n"; }
421 my $dbxref_id = $dbxref->store();
422 $self->set_dbxref_id($dbxref_id);
423 $self->d("Inserted new dbxref for accession " . $self->get_db_name() . ":" . $self->get_accession ."\n");
426 my $query = "INSERT INTO cvterm (cv_id, name, dbxref_id, definition, is_obsolete, is_relationshiptype) VALUES (?, ?, ?, ?,?,?)";
428 my $sth = $self->get_dbh()->prepare($query);
429 $sth->execute($self->get_cv_id(), $self->get_cvterm_name(), $self->get_dbxref_id(), $self->get_definition(), $self->get_obsolete(), $self->get_is_relationshiptype());
431 $cvterm_id = $self->get_dbh()->last_insert_id("cvterm", "public");
432 $self->set_cvterm_id($cvterm_id);
434 return $cvterm_id;
440 =head2 Class properties
442 The following class properties have accessors (get_cvterm_id, set_cvterm_id...):
444 cvterm_id
445 cv_id
446 cv_name
447 cvterm_name
448 definition
449 dbxref_id
450 accession
451 version
452 db_name
453 obsolete
454 is_relationshiptype
456 =cut
458 sub get_cvterm_id {
459 my $self=shift;
460 return $self->{cvterm_id};
463 sub set_cvterm_id {
464 my $self=shift;
465 $self->{cvterm_id}=shift;
468 sub get_cv_id {
469 my $self=shift;
470 return $self->{cv_id};
474 sub set_cv_id {
475 my $self=shift;
476 $self->{cv_id}=shift;
479 sub get_cv_name {
480 my $self=shift;
481 return $self->{cv_name};
485 sub set_cv_name {
486 my $self=shift;
487 $self->{cv_name}=shift;
490 sub get_cvterm_name {
491 my $self=shift;
492 return $self->{cvterm_name};
496 sub set_cvterm_name {
497 my $self=shift;
498 $self->{cvterm_name}=shift;
501 sub get_definition {
502 my $self=shift;
503 return $self->{definition};
507 sub set_definition {
508 my $self=shift;
509 $self->{definition}=shift;
513 sub get_dbxref_id {
514 my $self=shift;
515 return $self->{dbxref_id};
519 sub set_dbxref_id {
520 my $self=shift;
521 $self->{dbxref_id}=shift;
524 =head2 get_dbxref
526 Usage: my $self->get_dbxref();
527 Desc: get a dbxref object associated with the cvterm
528 Ret: a dbxref object
529 Args: none
530 Side Effects: none
531 Example:
533 =cut
535 sub get_dbxref {
536 my $self = shift;
537 return CXGN::Chado::Dbxref->new($self->get_dbh(), $self->get_dbxref_id());
540 sub get_accession {
541 my $self=shift;
542 return $self->{accession};
545 sub set_accession {
546 my $self=shift;
547 $self->{accession}=shift;
549 sub get_version {
550 my $self=shift;
551 return $self->{version};
554 sub set_version {
555 my $self=shift;
556 $self->{version}=shift;
559 sub get_db_name {
560 my $self=shift;
561 return $self->{db_name};
565 sub set_db_name {
566 my $self=shift;
567 $self->{db_name}=shift;
570 sub get_obsolete {
571 my $self=shift;
572 return $self->{obsolete};
575 sub set_obsolete {
576 my $self=shift;
577 $self->{obsolete}=shift;
581 sub get_is_relationshiptype {
582 my $self=shift;
583 return $self->{is_relationshiptype} || 0;
587 sub set_is_relationshiptype {
588 my $self=shift;
589 $self->{is_relationshiptype}=shift;
593 =head2 get_full_accession
595 Usage: $self->get_full_accession()
596 Desc: Usse this accessor to find the full accession of your cvterm
597 instead of concatenating db_name and accession (e.g. GO:0001234)
598 Ret: db_name:accession
599 Args: none
600 Side Effects: none
601 Example:
603 =cut
605 sub get_full_accession {
606 my $self=shift;
607 return $self->get_db_name() . ":" . $self->get_accession();
611 =head2 get_recursive_parents
613 Usage: my @parents = $self->get_recursive_parents()
614 Desc: a method for finding all the parents up to the root of the cvterm, and the cvterm-parent relationship
615 Ret: a list of listrefs containing CXGN::Chado::Cvterm objects and relationship types
616 Args: none
617 Side Effects: none
618 Example:
620 =cut
623 sub get_recursive_parents {
625 my $self=shift;
627 my $parents_q = "SELECT distinct(cvtermpath.object_id)
628 FROM cvtermpath
629 JOIN cvterm ON (cvtermpath.object_id = cvterm_id)
630 WHERE cvtermpath.subject_id =? AND cvterm.is_obsolete=0 AND pathdistance>0";
632 my $type_q = "SELECT type_id FROM cvterm_relationship
633 WHERE subject_id = ? AND object_id = ?";
634 my $type_sth = $self->get_dbh->prepare($type_q);
635 my $parents_sth = $self->get_dbh()->prepare($parents_q);
637 $parents_sth->execute($self->get_cvterm_id() );
638 my @parents = ();
639 while (my ($parent_term_id) = $parents_sth->fetchrow_array()) {
640 my $parent_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $parent_term_id);
641 $type_sth->execute($self->get_cvterm_id, $parent_term_id);
642 my ($type_id) = $type_sth->fetchrow_array();
643 my $relationship_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $type_id);
645 push @parents, [ $parent_term, $relationship_term ];
647 return (@parents);
650 =head2 get_parents
652 Usage: $self->get_parents
653 Desc: find the direct parents of the cvterm
654 Ret: a list of listrefs containing CXGN::Chado::Cvterm objects and relationship types
655 Args: none
656 Side Effects: none
657 Example:
659 =cut
661 sub get_parents {
662 my $self=shift;
663 my $parents_q = "SELECT object_id , type_id
664 FROM cvterm_relationship
665 WHERE subject_id = ? ";
666 my $parents_sth = $self->get_dbh()->prepare($parents_q);
667 $parents_sth->execute($self->get_cvterm_id() );
668 my @parents = ();
669 while (my ($parent_term_id, $type_id) = $parents_sth->fetchrow_array()) {
670 my $parent_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $parent_term_id);
671 my $relationship_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $type_id);
673 push @parents, [ $parent_term, $relationship_term ];
675 return (@parents);
678 =head2 get_children
680 Usage: my @children = $self->get_children()
681 Desc: a method for finding all the child terms of a cv term and their relationship
682 Ret: a list of lists with two elements: a cvterm object for the child and a
683 cvterm object for the relationship
684 Args: none
685 Side Effects:
686 Example:
688 =cut
690 sub get_children {
692 my $self=shift;
693 my $children_q = "select subject_id , type_id , name from cvterm_relationship join cvterm on (subject_id = cvterm_id ) where cv_id = ? AND object_id =? ";
695 my $children_sth= $self->get_dbh()->prepare($children_q);
696 $children_sth->execute( $self->get_cv_id, $self->get_cvterm_id() );
697 $self->d( "Parent cvterm id = ".$self->get_cvterm_id()."\n" );
698 my @children = ();
699 while (my ($child_term_id, $type_id, $cvterm_name) = $children_sth->fetchrow_array()) {
700 $self->d( "retrieved child $child_term_id, $type_id\n" );
701 my $child_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $child_term_id);
702 my $relationship_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $type_id);
703 push @children, [ $child_term, $relationship_term ];
705 return (@children);
709 =head2 get_recursive_children
711 Usage: $self->get_recursive_children
712 Desc: find all recursive child terms of this cvterm
713 Ret: a list of lists with two elements: a cvterm object for the child and a
714 cvterm object for the relationship
715 Args: none
716 Side Effects: none
717 Example:
719 =cut
721 sub get_recursive_children {
722 my $self=shift;
723 my $q = "SELECT distinct(cvtermpath.subject_id)
724 FROM cvtermpath
725 JOIN cvterm ON (cvtermpath.object_id = cvterm_id)
726 WHERE cvtermpath.object_id =? AND cvterm.is_obsolete=0 AND pathdistance>0 ";
728 my $sth = $self->get_dbh()->prepare($q);
729 $sth->execute($self->get_cvterm_id() );
731 my $type_q = "SELECT type_id FROM cvterm_relationship
732 WHERE subject_id = ? AND object_id = ?";
733 my $type_sth = $self->get_dbh->prepare($type_q);
735 my @children = ();
736 while (my ($child_term_id) = $sth->fetchrow_array()) {
737 my $child_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $child_term_id);
738 $type_sth->execute($child_term_id, $self->get_cvterm_id);
739 my ($type_id) = $type_sth->fetchrow_array();
740 my $relationship_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $type_id);
741 push @children, [ $child_term, $relationship_term ];
743 return (@children);
747 =head2 count_children
749 Usage: my $childrenNumber = $self->count_children()
750 Desc: a method for fetching the number of children of a cvterm
751 Ret: the number of children for the current db name (this is to avoid counting InterPro children of GO terms)
752 Args: none
753 Side Effects: none
754 Example:
756 =cut
758 sub count_children {
759 my $self = shift;
760 my $childNumber = 0;
762 my $child_sth = $self->get_dbh()->prepare("SELECT count( cvterm_relationship.subject_id )FROM cvterm_relationship join cvterm on cvterm.cvterm_id = cvterm_relationship.subject_id JOIN dbxref USING (dbxref_id) WHERE cvterm_relationship.object_id= ? and cvterm.is_obsolete = 0 ");
763 $child_sth->execute($self->get_cvterm_id() );
764 ($childNumber) = $child_sth->fetchrow_array();
765 return $childNumber;
770 =head2 get_synonyms
772 Usage: my @synonyms = $self->get_synonyms()
773 Desc: a method for fetching all synonyms of a cvterm
774 Ret: an array of synonyms
775 Args: none
776 Side Effects: none
777 Example:
779 =cut
781 sub get_synonyms {
782 my $self=shift;
784 my $cvterm_id= $self->get_cvterm_id();
785 my $query= "SELECT synonym FROM cvtermsynonym WHERE cvterm_id= ?";
787 my $synonym_sth = $self->get_dbh()->prepare($query);
789 $synonym_sth->execute($cvterm_id);
790 my @synonyms = ();
791 while (my ($synonym) = $synonym_sth->fetchrow_array()) {
792 push @synonyms, $synonym;
794 return @synonyms;
797 =head2 get_synonym_name
799 Usage: $self->get_synonym_name();
800 Desc: an alias for get_synonyms
801 Ret: an array of synonym names
802 Args: none
803 Side Effects:
804 Example:
806 =cut
808 sub get_synonym_name {
809 my $self=shift;
810 my @synonyms=$self->get_synonyms();
811 return @synonyms;
815 =head2 add_synonym
817 Usage: $t->add_synonym($new_synonym);
818 Desc: adds the synonym $new_synonym to the term $t.
819 If the synonym $new_synonym already exists,
820 nothing is added.
821 Note that in order to call add_synonym(), the
822 term needs to be stored in the database, otherwise
823 an error will occur.
824 Side Effects: accesses the database. Debug message.
825 Example:
827 =cut
829 sub add_synonym {
830 my $self = shift;
831 my $synonym = shift;
832 if (!$self->has_synonym($synonym)) {
833 my $query = "INSERT INTO cvtermsynonym (cvterm_id, synonym) VALUES (?, ?)";
834 my $sth = $self->get_dbh()->prepare($query);
835 $sth->execute($self->get_cvterm_id(), $synonym);
836 $self->d( "Cvterm.pm: adding synonym '$synonym' to cvterm ". $self->get_cvterm_name() ."\n") ;
838 else {
839 $self->d("$synonym is already a synonym of term ".($self->get_cvterm_name())."\n");
845 =head2 has_synonym
847 Usage: my $flag = $t->has_synonym("gobbledegook");
848 Desc: returns true if the synonym exists, false otherwise
849 Ret: 1 or 0
850 Args: a synonym name
851 Side Effects: none
852 Example:
854 =cut
856 sub has_synonym {
857 my $self = shift;
858 my $putative_synonym = shift;
859 my $synonym_sth = $self->get_dbh()->prepare("SELECT synonym FROM cvtermsynonym WHERE
860 cvterm_id= ? and synonym ilike ?");
861 $synonym_sth->execute($self->get_cvterm_id(), $putative_synonym);
862 my ($synonym) = $synonym_sth->fetchrow_array();
863 if ($synonym) {
864 return 1;
866 else {
867 return 0;
871 =head2 delete_synonym
873 Usage: $cvterm->delete_synonym($synonym)
874 Desc: delete synonym $synonym from cvterm object
875 Ret: nothing
876 Args: $synonym
877 Side Effects: accesses the database
878 Example:
880 =cut
882 sub delete_synonym {
883 my $self=shift;
884 my $synonym=shift;
885 my $query = "DELETE FROM cvtermsynonym WHERE cvterm_id= ? AND synonym = ?";
886 my $sth=$self->get_dbh()->prepare($query);
887 $sth->execute($self->get_cvterm_id(), $synonym);
892 =head2 term_is_obsolete
894 Usage: my $obsolete = $self->term_is_obsolete($cvterm_id);
895 Desc: a method for determining if a term is obsolete
896 Ret: false if not obsolete, true if obsolete
897 Args: $cvterm_id - an id of a controlled vocabulary term
898 Side Effects: none
899 Example:
901 =cut
903 sub term_is_obsolete {
904 my $self=shift;
906 my $cvterm_id=shift;
907 my $obsolete_sth = $self->get_dbh()->prepare("SELECT is_obsolete FROM cvterm WHERE cvterm_id= ?");
908 $obsolete_sth->execute($self->get_cvterm_id() );
909 my ($obsolete) = $obsolete_sth->fetchrow_array();
911 if( $obsolete == 1 ) {
912 return "true";
913 } else {
914 return "false";
918 =head2 associate_feature
920 Usage: $cvterm->associate_feature($feature_id, $pub_id)
921 Desc: associates the feature with $feature_id to the
922 cvterm.
923 Ret: nothing
924 Args: feature_id and a pub_id
925 Side Effects: accesses the database
926 Example:
928 =cut
930 sub associate_feature {
931 my $self = shift;
932 my $feature_id = shift;
933 my $pub_id = shift;
934 if (!($pub_id && $feature_id)) {
935 die "[CXGN::Chado::Cvterm] associate_feature(): Need feature_id and pub_id\n";
938 my $query = "INSERT INTO cvterm_feature (cvterm_id, feature_id, pub_id) VALUES (?, ?, ?)";
939 my $sth = $self->get_dbh()->prepare($query);
940 $sth->execute($self->get_cvterm_id(), $feature_id, $pub_id);
943 =head2 get_features
945 Usage: my @features = $cvterm->get_features()
946 Desc: returns a list of feature objects that are associated to this
947 cvterm using cvterm_feature table.
948 Ret: a list of CXGN::Chado::Feature objects
949 Args: none
950 Side Effects: accesses the database
951 Example:
953 =cut
955 sub get_features {
956 my $self = shift;
957 my $query = "SELECT feature_id FROM cvterm_feature WHERE cvterm_id=?";
958 my $sth = $self->get_dbh()->prepare($query);
959 $sth->execute($self->get_cvterm_id());
960 my @features = ();
961 while (my ($feature_id) = $sth->fetchrow_array()) {
962 push @features, CXGN::Chado::Feature->new($self->get_dbh(), $feature_id);
964 return @features;
967 =head2 add_secondary_dbxref
969 Usage: $self->add_secondary_dbxref(accession)
970 Desc: add an alternative id to cvterm. Stores in cvterm_dbxref
971 Ret: nothing
972 Args: an alternative id (i.e. "GO:0001234")
973 Side Effects: stors a new dbxref if accession is not found in dbxref table
974 Example:
976 =cut
978 sub add_secondary_dbxref {
979 my $self=shift;
980 my $accession=shift;
981 my ($db_name, $acc) = split (/:/, $accession);
982 my $db=CXGN::Chado::Db->new_with_name($self->get_dbh(), $db_name);
983 if ( !($db->get_db_id()) ) {
984 $db->set_db_name($db_name);
985 $self->d( "Cvterm.pm: Storing a new DB: $db_name\n");
986 $db->store();
988 #check is $accession exists:
989 my $dbxref_id= CXGN::Chado::Dbxref::get_dbxref_id_by_db_id($self->get_dbh(), $acc, $db->get_db_id());
990 if (!$dbxref_id) {
991 $self->d("No dbxref_id found for db_name '$db_name' accession '$acc' adding new dbxref...\n");
992 my $dbxref=CXGN::Chado::Dbxref->new($self->get_dbh());
993 $dbxref->set_accession($acc);
994 $dbxref->set_db_name($db->get_db_name());
995 $dbxref->store();
996 $dbxref_id=$dbxref->get_dbxref_id();
998 if (!$self->has_secondary_dbxref($dbxref_id) ) {
999 my $query = "INSERT INTO cvterm_dbxref (cvterm_id, dbxref_id) VALUES (?,?)";
1000 my $sth=$self->get_dbh()->prepare($query);
1001 $sth->execute($self->get_cvterm_id(), $dbxref_id);
1002 $self->d( "Cvterm.pm: adding secondary id '$accession' to cvterm ". $self->get_cvterm_name() . "\n");
1003 }else {
1004 $self->d("Cvterm.pm: $dbxref_id ($accession) is already a secondary id of term '".($self->get_cvterm_name())."'\n");
1008 =head2 has_secondary_dbxref
1010 Usage: $self->has_secondary_dbxref($dbxref_id)
1011 Desc: checks in the database if dbxref_id os already associated with the cvterm
1012 Ret: 1 or 0
1013 Args: dbxref_id
1014 Side Effects: none
1015 Example:
1017 =cut
1019 sub has_secondary_dbxref {
1020 my $self=shift;
1021 my $dbxref_id=shift;
1022 my $query = "SELECT cvterm_dbxref_id FROM cvterm_dbxref WHERE cvterm_id= ? AND dbxref_id= ?";
1023 my $sth=$self->get_dbh()->prepare($query);
1024 $sth->execute($self->get_cvterm_id(), $dbxref_id);
1025 my ($id) = $sth->fetchrow_array();
1026 if ($id) { return 1; }
1027 else { return 0; }
1030 =head2 get_secondary_dbxrefs
1032 Usage: $self->get_secondary_dbxrefs()
1033 Desc: find all secondary accessions associated with the cvterm
1034 These are stored in cvterm_dbxref table as dbxref_ids
1035 Ret: an array of accessions (PO:0001234)
1036 Args: none
1037 Side Effects: none
1038 Example:
1040 =cut
1042 sub get_secondary_dbxrefs {
1043 my $self=shift;
1044 my $query= "SELECT dbxref_id FROM cvterm_dbxref WHERE cvterm_id=? AND is_for_definition = 0";
1045 my $sth=$self->get_dbh()->prepare($query);
1046 $sth->execute($self->get_cvterm_id() );
1047 my @secondary;
1048 while (my ($dbxref_id) = $sth->fetchrow_array() ) {
1049 my $dbxref= CXGN::Chado::Dbxref->new($self->get_dbh(),$dbxref_id);
1050 my $accession = $dbxref->get_db_name() . ":" . $dbxref->get_accession();
1051 push @secondary, $accession;
1053 return @secondary;
1056 =head2 delete_secondary_dbxref
1058 Usage: $self->delete_secondary_dbxref()
1059 Desc: delete a cvterm_dbxref from the database
1060 Ret: nothing
1061 Args: accession (PO:0001234)
1062 Side Effects:
1063 Example:
1065 =cut
1067 sub delete_secondary_dbxref {
1068 my $self=shift;
1069 my $accession=shift;
1070 my ($db_name, $acc) = split (/:/, $accession);
1071 my $query= "DELETE FROM cvterm_dbxref where cvterm_id=? AND is_for_definition = 0
1072 AND dbxref_id=(SELECT dbxref_id FROM dbxref WHERE db_id= (SELECT db_id FROM db WHERE name =?)
1073 AND accession = ?)";
1074 my $sth=$self->get_dbh()->prepare($query);
1075 $sth->execute($self->get_cvterm_id(), $db_name, $accession);
1080 =head2 add_def_dbxref
1082 Usage: $self->add_def_dbxref($dbname, $accession)
1083 Desc: add a cvterm definition dbxref to cvterm_dbxref
1084 Ret: nothing
1085 Args: a db name and a dbxref accession
1086 Side Effects: stores a new db and a new dbxref if $dbname or $accession
1087 do not exist in db and/or dbxref tables
1088 Example:
1090 =cut
1092 sub add_def_dbxref {
1093 my $self=shift;
1094 my $dbname=shift;
1095 my $accession=shift;
1096 #check if $dbname exist:
1097 my $db=CXGN::Chado::Db->new_with_name($self->get_dbh(), $dbname);
1098 if ( !($db->get_db_id()) ) {
1099 $db->set_db_name($dbname);
1100 $self->d( "Cvterm.pm: Storing a new DB: $dbname\n");
1101 $db->store();
1103 #check is $accession exists:
1104 my $dbxref_id= CXGN::Chado::Dbxref::get_dbxref_id_by_db_id($self->get_dbh(), $accession, $db->get_db_id());
1105 if (!$dbxref_id) {
1106 my $dbxref=CXGN::Chado::Dbxref->new($self->get_dbh());
1107 $dbxref->set_db_name($db->get_db_name());
1108 $dbxref->set_accession($accession);
1109 $self->d( "Cvterm.pm: Storing a new Dbxref for db $dbname: $accession\n");
1111 $dbxref_id=$dbxref->store();
1113 if (!$self->has_secondary_dbxref($dbxref_id)) {
1114 my $query = "INSERT INTO cvterm_dbxref (cvterm_id, dbxref_id, is_for_definition)
1115 VALUES (?,?,1)";
1116 my $sth=$self->get_dbh()->prepare($query);
1117 $sth->execute($self->get_cvterm_id(), $dbxref_id);
1118 $self->d( "Cvterm.pm: Storing a new definition dbxref ($dbname:$accession) for cvterm". $self->get_cvterm_name() . "\n");
1120 else {
1121 my $query = "UPDATE cvterm_dbxref set is_for_definition=1
1122 WHERE cvterm_id=? and dbxref_id=?";
1123 my $sth=$self->get_dbh()->prepare($query);
1124 $sth->execute($self->get_cvterm_id(), $dbxref_id);
1129 =head2 get_def_dbxref
1131 Usage: $self->get_def_dbxref();
1132 Desc: find the definition dbxrefs of the cvterm (stored in cvterm_dbxref)
1133 Ret: an array of dbxref object
1134 Args: none
1135 Side Effects: none
1136 Example:
1138 =cut
1140 sub get_def_dbxref {
1141 my $self=shift;
1143 my $query = "SELECT dbxref_id FROM cvterm_dbxref WHERE cvterm_id=? and is_for_definition =1";
1144 my $sth=$self->get_dbh()->prepare($query);
1145 $sth->execute($self->get_cvterm_id());
1146 my @dbxrefs=();
1147 while (my ($dbxref_id) = $sth->fetchrow_array() ) {
1148 my $dbxref=CXGN::Chado::Dbxref->new($self->get_dbh(), $dbxref_id);
1149 push @dbxrefs, $dbxref;
1151 return @dbxrefs;
1155 =head2 delete_def_dbxref
1157 Usage: $self->delete_def_dbxref($dbxref)
1158 Desc: remove from the database a cvterm_dbxref
1159 Ret: nothing
1160 Args: dbxref object
1161 Side Effects: accesses the database
1162 Example:
1164 =cut
1166 sub delete_def_dbxref {
1167 my $self=shift;
1168 my $dbxref=shift;
1169 my $query = "DELETE FROM cvterm_dbxref WHERE cvterm_id=? AND dbxref_id=? AND is_for_definition = 1";
1170 my $sth=$self->get_dbh()->prepare($query);
1172 $sth->execute($self->get_cvterm_id(), $dbxref->get_dbxref_id());
1175 =head2 get_cvterm_by_name
1177 Usage: CXGN::Chado::Cvterm::get_cvterm_by_name($dbh, $name)
1178 Desc: get a cvterm object with name $name
1179 Ret: cvterm object. Empty object if name does not exist in cvterm table
1180 Args: database handle and a cvterm name (and '1' if you want to check for an existing relationship type! )
1181 Side Effects: none
1182 Example:
1184 =cut
1186 sub get_cvterm_by_name {
1187 my $dbh=shift;
1188 my $name=shift;
1189 my $is_rel=shift; #optional!
1190 my $query = "SELECT cvterm_id FROM public.cvterm WHERE name ilike ?";
1191 $query .=" AND is_relationshiptype =1 " if $is_rel;
1192 my $sth=$dbh->prepare($query);
1193 $sth->execute($name);
1194 my ($cvterm_id) = $sth->fetchrow_array();
1195 my $cvterm= CXGN::Chado::Cvterm->new($dbh, $cvterm_id);
1196 return $cvterm;
1199 =head2 cvterm_exists
1201 Usage: $self->cvterm_exists()
1202 Desc: check if another cvterm exists with the same cv_id, name, and is_obsolete value
1203 prior to updating
1204 Ret: cvterm_id or undef if no other cvterm exists
1205 Args: non
1206 Side Effects: none
1207 Example:
1209 =cut
1211 sub cvterm_exists {
1213 my $self=shift;
1214 my $cvterm_id= $self->get_cvterm_id();
1215 my $query="SELECT cvterm_id FROM public.cvterm WHERE cv_id=? AND name=? AND is_obsolete=?";
1216 my $sth=$self->get_dbh()->prepare($query);
1217 $sth->execute($self->get_cv_id(), $self->get_cvterm_name(), $self->get_obsolete());
1218 my ($existing_cvterm_id) = $sth->fetchrow_array();
1219 if ($cvterm_id == $existing_cvterm_id) { return undef; }
1220 else { return $existing_cvterm_id ; }
1225 =head2 obsolete
1227 Usage: $self->obsolete()
1228 Desc: set a cvterm is_obsolete = 1
1229 Ret: nothing
1230 Args: none
1231 Side Effects: accesses the database
1232 Example:
1234 =cut
1236 sub obsolete {
1237 my $self=shift;
1238 if ($self->get_cvterm_id() ) {
1239 my $query= "UPDATE public.cvterm SET is_obsolete = 1 WHERE cvterm_id=?";
1240 my $sth=$self->get_dbh()->prepare($query);
1241 $sth->execute($self->get_cvterm_id() );
1242 }else {
1243 $self->d( "Trying to obsolete a term that hasn't been stored yet! \n");
1247 =head2 get_alt_id
1249 Usage: $self->get_alt_id();
1250 Desc: find the alternative id of a term. Meant to be used for finding
1251 an alternative cvterm for an obsolete term
1252 Ret: list of dbxref_ids or undef
1253 Args: none
1254 Side Effects: none
1255 Example:
1257 =cut
1259 sub get_alt_id {
1260 my $self=shift;
1261 my $query = "SELECT cvterm.dbxref_id FROM cvterm WHERE cvterm_id IN
1262 (SELECT cvterm_id FROM cvterm_dbxref WHERE dbxref_id= ?)";
1263 my @alt_ids;
1264 my $sth=$self->get_dbh()->prepare($query);
1265 $sth->execute($self->get_dbxref_id() );
1266 while (my ($alt_id) = $sth->fetchrow_array()) {
1267 push @alt_ids, $alt_id ;
1269 return @alt_ids || undef ;
1272 =head2 map_to_slim
1274 Usage: my @match_list = $cvterm->map_to_slim(@term_list)
1275 Desc: returns a list of terms in the @term_list that
1276 are parents of the term object. This function is
1277 useful for mapping terms to a slim vocabulary.
1278 Ret: a list of term identifiers
1279 Args: a list of term identifiers that are in the slim vocabulary
1280 Side Effects: accesses the database
1281 Note: the db name is stripped off if provided (GO:0003832 is
1282 given as 0003832)
1283 Example:
1285 =cut
1287 sub map_to_slim {
1288 my $self = shift;
1289 my @slim = @_;
1291 my %slim_counts = ();
1292 for (my $i=0; $i<@slim; $i++) {
1294 # strip db name off id
1296 $slim[$i]=~s/.*?(\d+).*/$1/;
1298 # make a unique list of slim terms
1300 $slim_counts{$slim[$i]}=0;
1302 $self->get_slim_counts(\%slim_counts);
1304 print Data::Dumper::Dumper(\%slim_counts);
1306 my @matches = ();
1307 foreach my $k (keys %slim_counts) {
1308 if ($slim_counts{$k}>0) { push @matches, $k; }
1310 return @matches;
1313 sub get_slim_counts {
1314 my $self = shift;
1315 my $slim_counts = shift;
1317 my $id = $self->identifier();
1319 if (exists($slim_counts->{$id}) && defined($slim_counts->{$id})) {
1320 $slim_counts->{$id}++;
1321 return;
1324 foreach my $p ($self->get_parents()) {
1325 $p->[0]->get_slim_counts($slim_counts);
1330 # sub get_slim_counts {
1331 # my $self = shift;
1332 # my $slim_counts = shift;
1334 # foreach my $p ($self->get_parents()) {
1335 # my $id = $p->[0]->identifier();
1336 # $self->d("Checking $id\n");
1337 # if (exists($slim_counts->{$id}) && defined($slim_counts->{$id})) {
1338 # $slim_counts->{$id}++;
1340 # else {
1341 # $p->[0]->get_slim_counts($slim_counts);
1346 # return $slim_counts;
1349 # # sub get_slim_term {
1350 # my $self = shift;
1351 # my $slim_counts = shift;
1353 # my $slim = "";
1354 # foreach my $p ($self->get_parents()) {
1355 # my $id = $p->[0]->identifier();
1356 # if (exists($slim_counts->{$id}) && defined($slim_counts->{$id})) {
1357 # $slim = $id;
1358 # last();
1361 # return $slim;
1366 =head2 get_all_populations_cvterm
1368 Usage: my @pops = $cvterm->get_all_populations_cvterm();
1369 Desc: returns a list of populations phenotyped for the particular trait (cvterm).
1370 Ret: list of population objects
1371 Args: none
1372 Side Effects: accesses database
1373 Example:
1375 =cut
1377 sub get_all_populations_cvterm {
1378 my $self=shift;
1379 my $query = "SELECT DISTINCT(phenome.population.population_id) FROM public.phenotype
1381 LEFT JOIN phenome.individual USING (individual_id)
1382 LEFT JOIN phenome.population USING (population_id)
1383 WHERE observable_id = ?";
1384 my $sth=$self->get_dbh->prepare($query);
1385 $sth->execute($self->get_cvterm_id());
1386 my @populations;
1387 while (my ($pop_id) = $sth->fetchrow_array()) {
1388 my $pop = CXGN::Phenome::Population->new($self->get_dbh(), $pop_id);
1389 push @populations, $pop;
1391 return @populations;
1397 =head2 get_individuals
1399 Usage: $self->get_individuals
1400 Desc: find all individuals annotated with this cvterm
1401 Ret: list of Individual objects
1402 Args: none
1403 Side Effects: none
1404 Example:
1406 =cut
1408 sub get_individuals {
1409 my $self=shift;
1410 my $query = "SELECT individual_id FROM phenome.individual
1411 JOIN phenome.individual_dbxref USING (individual_id)
1412 WHERE dbxref_id=?";
1413 my $sth=$self->get_dbh()->prepare($query);;
1414 $sth->execute($self->get_dbxref_id);
1415 my @individuals;
1416 while ( my ($individual_id) = $sth->fetchrow_array() ) {
1417 my $ind= CXGN::Phenome::Individual->new($self->get_dbh(), $individual_id);
1418 push @individuals, $ind;
1420 return @individuals;
1423 =head2 get_loci
1425 Usage: $self->get_loci()
1426 Desc: find the loci with annotations of this cvterm
1427 Ret: a list of Locus objects
1428 Args: none
1429 Side Effects: none
1430 Example:
1432 =cut
1434 sub get_loci {
1435 my $self=shift;
1436 my $query = "SELECT locus_id FROM phenome.locus
1437 JOIN phenome.locus_dbxref USING (locus_id)
1438 WHERE dbxref_id=?
1439 ORDER BY locus.locus_symbol";
1440 my $sth=$self->get_dbh()->prepare($query);;
1441 $sth->execute($self->get_dbxref_id);
1442 my @loci;
1443 while ( my ($locus_id) = $sth->fetchrow_array() ) {
1444 my $locus= CXGN::Phenome::Locus->new($self->get_dbh(), $locus_id);
1445 push @loci, $locus;
1447 return @loci;
1450 =head2 get_recursive_loci
1452 Usage: my @loci= $self->get_recursive_loci()
1453 Desc: find all the loci annotated with the cvterm or any of its recursive children
1454 Ret: a list of locus ojects
1455 Args: none
1456 Side Effects: none
1457 Example:
1459 =cut
1461 sub get_recursive_loci {
1462 my $self=shift;
1463 my $query = "select distinct locus_id from cvtermpath join cvterm on (cvtermpath.object_id = cvterm.cvterm_id or cvtermpath.subject_id = cvterm.cvterm_id) join phenome.locus_dbxref using (dbxref_id ) join phenome.locus using (locus_id) where (cvtermpath.object_id = ?) and locus_dbxref.obsolete = 'f' and locus.obsolete = 'f' and pathdistance > 0";
1465 my $sth=$self->get_dbh()->prepare($query);
1466 my @loci;
1467 $sth->execute($self->get_cvterm_id() );
1468 while ( my ($locus_id) = $sth->fetchrow_array() ) {
1469 my $locus= CXGN::Phenome::Locus->new($self->get_dbh(), $locus_id);
1470 push @loci, $locus;
1473 return @loci;
1476 =head2 get_recursive_individuals
1478 Usage: my @ind= $self->get_recursive_individuals()
1479 Desc: find all the individuals annotated with the cvterm or any of its recursive children
1480 Ret: a list of Individual ojects
1481 Args: none
1482 Side Effects: none
1483 Example:
1485 =cut
1487 sub get_recursive_individuals {
1488 my $self=shift;
1490 my $query = "select distinct individual_id from cvtermpath join cvterm on (cvtermpath.object_id = cvterm.cvterm_id or cvtermpath.subject_id = cvterm.cvterm_id) join phenome.individual_dbxref using (dbxref_id ) join phenome.individual using (individual_id) where ( cvtermpath.object_id =?) and individual_dbxref.obsolete = 'f' and individual.obsolete = 'f' and pathdistance > 0 ";
1493 my $sth=$self->get_dbh()->prepare($query);;
1494 my @ind;
1495 $sth->execute($self->get_cvterm_id() );
1496 while ( my ($individual_id) = $sth->fetchrow_array() ) {
1497 my $individual= CXGN::Phenome::Individual->new($self->get_dbh(), $individual_id);
1498 push @ind, $individual;
1500 return @ind;
1508 1;#do not remove