removed duplicated function
[cxgn-corelibs.git] / lib / CXGN / Chado / Cvterm.pm
bloba983cd3018a071b774cae5339aa45dd77ecba27a
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 {
624 my $self=shift;
626 my $parents_q = "SELECT distinct(cvtermpath.object_id)
627 FROM cvtermpath
628 JOIN cvterm ON (cvtermpath.object_id = cvterm_id)
629 WHERE cvtermpath.subject_id =? AND cvterm.is_obsolete=0 AND pathdistance>0";
631 my $type_q = "SELECT type_id FROM cvterm_relationship
632 WHERE subject_id = ? AND object_id = ?";
633 my $type_sth = $self->get_dbh->prepare($type_q);
634 my $parents_sth = $self->get_dbh()->prepare($parents_q);
636 $parents_sth->execute($self->get_cvterm_id() );
637 my @parents = ();
638 while (my ($parent_term_id) = $parents_sth->fetchrow_array()) {
639 my $parent_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $parent_term_id);
640 $type_sth->execute($self->get_cvterm_id, $parent_term_id);
641 my ($type_id) = $type_sth->fetchrow_array();
642 my $relationship_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $type_id);
644 push @parents, [ $parent_term, $relationship_term ];
646 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_parents
680 Usage: $self->get_parents
681 Desc: find the direct parents of the cvterm
682 Ret: a list of listrefs containing CXGN::Chado::Cvterm objects and relationship types
683 Args: none
684 Side Effects: none
685 Example:
687 =cut
689 sub get_parents {
690 my $self=shift;
691 my $parents_q = "SELECT object_id , type_id
692 FROM cvterm_relationship
693 WHERE subject_id = ? ";
694 my $parents_sth = $self->get_dbh()->prepare($parents_q);
695 $parents_sth->execute($self->get_cvterm_id() );
696 my @parents = ();
697 while (my ($parent_term_id, $type_id) = $parents_sth->fetchrow_array()) {
698 my $parent_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $parent_term_id);
699 my $relationship_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $type_id);
701 push @parents, [ $parent_term, $relationship_term ];
703 return (@parents);
706 =head2 get_children
708 Usage: my @children = $self->get_children()
709 Desc: a method for finding all the child terms of a cv term and their relationship
710 Ret: a list of lists with two elements: a cvterm object for the child and a
711 cvterm object for the relationship
712 Args: none
713 Side Effects:
714 Example:
716 =cut
718 sub get_children {
720 my $self=shift;
722 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 =? ";
724 my $children_sth= $self->get_dbh()->prepare($children_q);
725 $children_sth->execute( $self->get_cv_id, $self->get_cvterm_id() );
727 $self->d( "Parent cvterm id = ".$self->get_cvterm_id()."\n" );
728 my @children = ();
729 while (my ($child_term_id, $type_id, $cvterm_name) = $children_sth->fetchrow_array()) {
730 $self->d( "retrieved child $child_term_id, $type_id\n" );
731 my $child_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $child_term_id);
732 my $relationship_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $type_id);
733 push @children, [ $child_term, $relationship_term ];
735 return (@children);
739 =head2 get_recursive_children
741 Usage: $self->get_recursive_children
742 Desc: find all recursive child terms of this cvterm
743 Ret: a list of lists with two elements: a cvterm object for the child and a
744 cvterm object for the relationship
745 Args: none
746 Side Effects: none
747 Example:
749 =cut
751 sub get_recursive_children {
752 my $self=shift;
753 my $q = "SELECT distinct(cvtermpath.subject_id)
754 FROM cvtermpath
755 JOIN cvterm ON (cvtermpath.object_id = cvterm_id)
756 WHERE cvtermpath.object_id =? AND cvterm.is_obsolete=0 AND pathdistance>0 ";
758 my $sth = $self->get_dbh()->prepare($q);
759 $sth->execute($self->get_cvterm_id() );
761 my $type_q = "SELECT type_id FROM cvterm_relationship
762 WHERE subject_id = ? AND object_id = ?";
763 my $type_sth = $self->get_dbh->prepare($type_q);
765 my @children = ();
766 while (my ($child_term_id) = $sth->fetchrow_array()) {
767 my $child_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $child_term_id);
768 $type_sth->execute($child_term_id, $self->get_cvterm_id);
769 my ($type_id) = $type_sth->fetchrow_array();
770 my $relationship_term = CXGN::Chado::Cvterm->new($self->get_dbh(), $type_id);
771 push @children, [ $child_term, $relationship_term ];
773 return (@children);
777 =head2 count_children
779 Usage: my $childrenNumber = $self->count_children()
780 Desc: a method for fetching the number of children of a cvterm
781 Ret: the number of children for the current db name (this is to avoid counting InterPro children of GO terms)
782 Args: none
783 Side Effects: none
784 Example:
786 =cut
788 sub count_children {
789 my $self = shift;
790 my $childNumber = 0;
792 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 ");
793 $child_sth->execute($self->get_cvterm_id() );
794 ($childNumber) = $child_sth->fetchrow_array();
795 return $childNumber;
800 =head2 get_synonyms
802 Usage: my @synonyms = $self->get_synonyms()
803 Desc: a method for fetching all synonyms of a cvterm
804 Ret: an array of synonyms
805 Args: none
806 Side Effects: none
807 Example:
809 =cut
811 sub get_synonyms {
812 my $self=shift;
814 my $cvterm_id= $self->get_cvterm_id();
815 my $query= "SELECT synonym FROM cvtermsynonym WHERE cvterm_id= ?";
817 my $synonym_sth = $self->get_dbh()->prepare($query);
819 $synonym_sth->execute($cvterm_id);
820 my @synonyms = ();
821 while (my ($synonym) = $synonym_sth->fetchrow_array()) {
822 push @synonyms, $synonym;
824 return @synonyms;
827 =head2 get_synonym_name
829 Usage: $self->get_synonym_name();
830 Desc: an alias for get_synonyms
831 Ret: an array of synonym names
832 Args: none
833 Side Effects:
834 Example:
836 =cut
838 sub get_synonym_name {
839 my $self=shift;
840 my @synonyms=$self->get_synonyms();
841 return @synonyms;
845 =head2 add_synonym
847 Usage: $t->add_synonym($new_synonym);
848 Desc: adds the synonym $new_synonym to the term $t.
849 If the synonym $new_synonym already exists,
850 nothing is added.
851 Note that in order to call add_synonym(), the
852 term needs to be stored in the database, otherwise
853 an error will occur.
854 Side Effects: accesses the database. Debug message.
855 Example:
857 =cut
859 sub add_synonym {
860 my $self = shift;
861 my $synonym = shift;
862 if (!$self->has_synonym($synonym)) {
863 my $query = "INSERT INTO cvtermsynonym (cvterm_id, synonym) VALUES (?, ?)";
864 my $sth = $self->get_dbh()->prepare($query);
865 $sth->execute($self->get_cvterm_id(), $synonym);
866 $self->d( "Cvterm.pm: adding synonym '$synonym' to cvterm ". $self->get_cvterm_name() ."\n") ;
868 else {
869 $self->d("$synonym is already a synonym of term ".($self->get_cvterm_name())."\n");
875 =head2 has_synonym
877 Usage: my $flag = $t->has_synonym("gobbledegook");
878 Desc: returns true if the synonym exists, false otherwise
879 Ret: 1 or 0
880 Args: a synonym name
881 Side Effects: none
882 Example:
884 =cut
886 sub has_synonym {
887 my $self = shift;
888 my $putative_synonym = shift;
889 my $synonym_sth = $self->get_dbh()->prepare("SELECT synonym FROM cvtermsynonym WHERE
890 cvterm_id= ? and synonym ilike ?");
891 $synonym_sth->execute($self->get_cvterm_id(), $putative_synonym);
892 my ($synonym) = $synonym_sth->fetchrow_array();
893 if ($synonym) {
894 return 1;
896 else {
897 return 0;
901 =head2 delete_synonym
903 Usage: $cvterm->delete_synonym($synonym)
904 Desc: delete synonym $synonym from cvterm object
905 Ret: nothing
906 Args: $synonym
907 Side Effects: accesses the database
908 Example:
910 =cut
912 sub delete_synonym {
913 my $self=shift;
914 my $synonym=shift;
915 my $query = "DELETE FROM cvtermsynonym WHERE cvterm_id= ? AND synonym = ?";
916 my $sth=$self->get_dbh()->prepare($query);
917 $sth->execute($self->get_cvterm_id(), $synonym);
922 =head2 term_is_obsolete
924 Usage: my $obsolete = $self->term_is_obsolete($cvterm_id);
925 Desc: a method for determining if a term is obsolete
926 Ret: false if not obsolete, true if obsolete
927 Args: $cvterm_id - an id of a controlled vocabulary term
928 Side Effects: none
929 Example:
931 =cut
933 sub term_is_obsolete {
934 my $self=shift;
936 my $cvterm_id=shift;
937 my $obsolete_sth = $self->get_dbh()->prepare("SELECT is_obsolete FROM cvterm WHERE cvterm_id= ?");
938 $obsolete_sth->execute($self->get_cvterm_id() );
939 my ($obsolete) = $obsolete_sth->fetchrow_array();
941 if( $obsolete == 1 ) {
942 return "true";
943 } else {
944 return "false";
948 =head2 associate_feature
950 Usage: $cvterm->associate_feature($feature_id, $pub_id)
951 Desc: associates the feature with $feature_id to the
952 cvterm.
953 Ret: nothing
954 Args: feature_id and a pub_id
955 Side Effects: accesses the database
956 Example:
958 =cut
960 sub associate_feature {
961 my $self = shift;
962 my $feature_id = shift;
963 my $pub_id = shift;
964 if (!($pub_id && $feature_id)) {
965 die "[CXGN::Chado::Cvterm] associate_feature(): Need feature_id and pub_id\n";
968 my $query = "INSERT INTO cvterm_feature (cvterm_id, feature_id, pub_id) VALUES (?, ?, ?)";
969 my $sth = $self->get_dbh()->prepare($query);
970 $sth->execute($self->get_cvterm_id(), $feature_id, $pub_id);
973 =head2 get_features
975 Usage: my @features = $cvterm->get_features()
976 Desc: returns a list of feature objects that are associated to this
977 cvterm using cvterm_feature table.
978 Ret: a list of CXGN::Chado::Feature objects
979 Args: none
980 Side Effects: accesses the database
981 Example:
983 =cut
985 sub get_features {
986 my $self = shift;
987 my $query = "SELECT feature_id FROM cvterm_feature WHERE cvterm_id=?";
988 my $sth = $self->get_dbh()->prepare($query);
989 $sth->execute($self->get_cvterm_id());
990 my @features = ();
991 while (my ($feature_id) = $sth->fetchrow_array()) {
992 push @features, CXGN::Chado::Feature->new($self->get_dbh(), $feature_id);
994 return @features;
997 =head2 add_secondary_dbxref
999 Usage: $self->add_secondary_dbxref(accession)
1000 Desc: add an alternative id to cvterm. Stores in cvterm_dbxref
1001 Ret: nothing
1002 Args: an alternative id (i.e. "GO:0001234")
1003 Side Effects: stors a new dbxref if accession is not found in dbxref table
1004 Example:
1006 =cut
1008 sub add_secondary_dbxref {
1009 my $self=shift;
1010 my $accession=shift;
1011 my ($db_name, $acc) = split (/:/, $accession);
1012 my $db=CXGN::Chado::Db->new_with_name($self->get_dbh(), $db_name);
1013 if ( !($db->get_db_id()) ) {
1014 $db->set_db_name($db_name);
1015 $self->d( "Cvterm.pm: Storing a new DB: $db_name\n");
1016 $db->store();
1018 #check is $accession exists:
1019 my $dbxref_id= CXGN::Chado::Dbxref::get_dbxref_id_by_db_id($self->get_dbh(), $acc, $db->get_db_id());
1020 if (!$dbxref_id) {
1021 $self->d("No dbxref_id found for db_name '$db_name' accession '$acc' adding new dbxref...\n");
1022 my $dbxref=CXGN::Chado::Dbxref->new($self->get_dbh());
1023 $dbxref->set_accession($acc);
1024 $dbxref->set_db_name($db->get_db_name());
1025 $dbxref->store();
1026 $dbxref_id=$dbxref->get_dbxref_id();
1028 if (!$self->has_secondary_dbxref($dbxref_id) ) {
1029 my $query = "INSERT INTO cvterm_dbxref (cvterm_id, dbxref_id) VALUES (?,?)";
1030 my $sth=$self->get_dbh()->prepare($query);
1031 $sth->execute($self->get_cvterm_id(), $dbxref_id);
1032 $self->d( "Cvterm.pm: adding secondary id '$accession' to cvterm ". $self->get_cvterm_name() . "\n");
1033 }else {
1034 $self->d("Cvterm.pm: $dbxref_id ($accession) is already a secondary id of term '".($self->get_cvterm_name())."'\n");
1038 =head2 has_secondary_dbxref
1040 Usage: $self->has_secondary_dbxref($dbxref_id)
1041 Desc: checks in the database if dbxref_id os already associated with the cvterm
1042 Ret: 1 or 0
1043 Args: dbxref_id
1044 Side Effects: none
1045 Example:
1047 =cut
1049 sub has_secondary_dbxref {
1050 my $self=shift;
1051 my $dbxref_id=shift;
1052 my $query = "SELECT cvterm_dbxref_id FROM cvterm_dbxref WHERE cvterm_id= ? AND dbxref_id= ?";
1053 my $sth=$self->get_dbh()->prepare($query);
1054 $sth->execute($self->get_cvterm_id(), $dbxref_id);
1055 my ($id) = $sth->fetchrow_array();
1056 if ($id) { return 1; }
1057 else { return 0; }
1060 =head2 get_secondary_dbxrefs
1062 Usage: $self->get_secondary_dbxrefs()
1063 Desc: find all secondary accessions associated with the cvterm
1064 These are stored in cvterm_dbxref table as dbxref_ids
1065 Ret: an array of accessions (PO:0001234)
1066 Args: none
1067 Side Effects: none
1068 Example:
1070 =cut
1072 sub get_secondary_dbxrefs {
1073 my $self=shift;
1074 my $query= "SELECT dbxref_id FROM cvterm_dbxref WHERE cvterm_id=? AND is_for_definition = 0";
1075 my $sth=$self->get_dbh()->prepare($query);
1076 $sth->execute($self->get_cvterm_id() );
1077 my @secondary;
1078 while (my ($dbxref_id) = $sth->fetchrow_array() ) {
1079 my $dbxref= CXGN::Chado::Dbxref->new($self->get_dbh(),$dbxref_id);
1080 my $accession = $dbxref->get_db_name() . ":" . $dbxref->get_accession();
1081 push @secondary, $accession;
1083 return @secondary;
1086 =head2 delete_secondary_dbxref
1088 Usage: $self->delete_secondary_dbxref()
1089 Desc: delete a cvterm_dbxref from the database
1090 Ret: nothing
1091 Args: accession (PO:0001234)
1092 Side Effects:
1093 Example:
1095 =cut
1097 sub delete_secondary_dbxref {
1098 my $self=shift;
1099 my $accession=shift;
1100 my ($db_name, $acc) = split (/:/, $accession);
1101 my $query= "DELETE FROM cvterm_dbxref where cvterm_id=? AND is_for_definition = 0
1102 AND dbxref_id=(SELECT dbxref_id FROM dbxref WHERE db_id= (SELECT db_id FROM db WHERE name =?)
1103 AND accession = ?)";
1104 my $sth=$self->get_dbh()->prepare($query);
1105 $sth->execute($self->get_cvterm_id(), $db_name, $accession);
1110 =head2 add_def_dbxref
1112 Usage: $self->add_def_dbxref($dbname, $accession)
1113 Desc: add a cvterm definition dbxref to cvterm_dbxref
1114 Ret: nothing
1115 Args: a db name and a dbxref accession
1116 Side Effects: stores a new db and a new dbxref if $dbname or $accession
1117 do not exist in db and/or dbxref tables
1118 Example:
1120 =cut
1122 sub add_def_dbxref {
1123 my $self=shift;
1124 my $dbname=shift;
1125 my $accession=shift;
1126 #check if $dbname exist:
1127 my $db=CXGN::Chado::Db->new_with_name($self->get_dbh(), $dbname);
1128 if ( !($db->get_db_id()) ) {
1129 $db->set_db_name($dbname);
1130 $self->d( "Cvterm.pm: Storing a new DB: $dbname\n");
1131 $db->store();
1133 #check is $accession exists:
1134 my $dbxref_id= CXGN::Chado::Dbxref::get_dbxref_id_by_db_id($self->get_dbh(), $accession, $db->get_db_id());
1135 if (!$dbxref_id) {
1136 my $dbxref=CXGN::Chado::Dbxref->new($self->get_dbh());
1137 $dbxref->set_db_name($db->get_db_name());
1138 $dbxref->set_accession($accession);
1139 $self->d( "Cvterm.pm: Storing a new Dbxref for db $dbname: $accession\n");
1141 $dbxref_id=$dbxref->store();
1143 if (!$self->has_secondary_dbxref($dbxref_id)) {
1144 my $query = "INSERT INTO cvterm_dbxref (cvterm_id, dbxref_id, is_for_definition)
1145 VALUES (?,?,1)";
1146 my $sth=$self->get_dbh()->prepare($query);
1147 $sth->execute($self->get_cvterm_id(), $dbxref_id);
1148 $self->d( "Cvterm.pm: Storing a new definition dbxref ($dbname:$accession) for cvterm". $self->get_cvterm_name() . "\n");
1150 else {
1151 my $query = "UPDATE cvterm_dbxref set is_for_definition=1
1152 WHERE cvterm_id=? and dbxref_id=?";
1153 my $sth=$self->get_dbh()->prepare($query);
1154 $sth->execute($self->get_cvterm_id(), $dbxref_id);
1159 =head2 get_def_dbxref
1161 Usage: $self->get_def_dbxref();
1162 Desc: find the definition dbxrefs of the cvterm (stored in cvterm_dbxref)
1163 Ret: an array of dbxref object
1164 Args: none
1165 Side Effects: none
1166 Example:
1168 =cut
1170 sub get_def_dbxref {
1171 my $self=shift;
1173 my $query = "SELECT dbxref_id FROM cvterm_dbxref WHERE cvterm_id=? and is_for_definition =1";
1174 my $sth=$self->get_dbh()->prepare($query);
1175 $sth->execute($self->get_cvterm_id());
1176 my @dbxrefs=();
1177 while (my ($dbxref_id) = $sth->fetchrow_array() ) {
1178 my $dbxref=CXGN::Chado::Dbxref->new($self->get_dbh(), $dbxref_id);
1179 push @dbxrefs, $dbxref;
1181 return @dbxrefs;
1185 =head2 delete_def_dbxref
1187 Usage: $self->delete_def_dbxref($dbxref)
1188 Desc: remove from the database a cvterm_dbxref
1189 Ret: nothing
1190 Args: dbxref object
1191 Side Effects: accesses the database
1192 Example:
1194 =cut
1196 sub delete_def_dbxref {
1197 my $self=shift;
1198 my $dbxref=shift;
1199 my $query = "DELETE FROM cvterm_dbxref WHERE cvterm_id=? AND dbxref_id=? AND is_for_definition = 1";
1200 my $sth=$self->get_dbh()->prepare($query);
1202 $sth->execute($self->get_cvterm_id(), $dbxref->get_dbxref_id());
1205 =head2 get_cvterm_by_name
1207 Usage: CXGN::Chado::Cvterm::get_cvterm_by_name($dbh, $name)
1208 Desc: get a cvterm object with name $name
1209 Ret: cvterm object. Empty object if name does not exist in cvterm table
1210 Args: database handle and a cvterm name (and '1' if you want to check for an existing relationship type! )
1211 Side Effects: none
1212 Example:
1214 =cut
1216 sub get_cvterm_by_name {
1217 my $dbh=shift;
1218 my $name=shift;
1219 my $is_rel=shift; #optional!
1220 my $query = "SELECT cvterm_id FROM public.cvterm WHERE name ilike ?";
1221 $query .=" AND is_relationshiptype =1 " if $is_rel;
1222 my $sth=$dbh->prepare($query);
1223 $sth->execute($name);
1224 my ($cvterm_id) = $sth->fetchrow_array();
1225 my $cvterm= CXGN::Chado::Cvterm->new($dbh, $cvterm_id);
1226 return $cvterm;
1229 =head2 cvterm_exists
1231 Usage: $self->cvterm_exists()
1232 Desc: check if another cvterm exists with the same cv_id, name, and is_obsolete value
1233 prior to updating
1234 Ret: cvterm_id or undef if no other cvterm exists
1235 Args: non
1236 Side Effects: none
1237 Example:
1239 =cut
1241 sub cvterm_exists {
1243 my $self=shift;
1244 my $cvterm_id= $self->get_cvterm_id();
1245 my $query="SELECT cvterm_id FROM public.cvterm WHERE cv_id=? AND name=? AND is_obsolete=?";
1246 my $sth=$self->get_dbh()->prepare($query);
1247 $sth->execute($self->get_cv_id(), $self->get_cvterm_name(), $self->get_obsolete());
1248 my ($existing_cvterm_id) = $sth->fetchrow_array();
1249 if ($cvterm_id == $existing_cvterm_id) { return undef; }
1250 else { return $existing_cvterm_id ; }
1255 =head2 obsolete
1257 Usage: $self->obsolete()
1258 Desc: set a cvterm is_obsolete = 1
1259 Ret: nothing
1260 Args: none
1261 Side Effects: accesses the database
1262 Example:
1264 =cut
1266 sub obsolete {
1267 my $self=shift;
1268 if ($self->get_cvterm_id() ) {
1269 my $query= "UPDATE public.cvterm SET is_obsolete = 1 WHERE cvterm_id=?";
1270 my $sth=$self->get_dbh()->prepare($query);
1271 $sth->execute($self->get_cvterm_id() );
1272 }else {
1273 $self->d( "Trying to obsolete a term that hasn't been stored yet! \n");
1277 =head2 get_alt_id
1279 Usage: $self->get_alt_id();
1280 Desc: find the alternative id of a term. Meant to be used for finding
1281 an alternative cvterm for an obsolete term
1282 Ret: list of dbxref_ids or undef
1283 Args: none
1284 Side Effects: none
1285 Example:
1287 =cut
1289 sub get_alt_id {
1290 my $self=shift;
1291 my $query = "SELECT cvterm.dbxref_id FROM cvterm WHERE cvterm_id IN
1292 (SELECT cvterm_id FROM cvterm_dbxref WHERE dbxref_id= ?)";
1293 my @alt_ids;
1294 my $sth=$self->get_dbh()->prepare($query);
1295 $sth->execute($self->get_dbxref_id() );
1296 while (my ($alt_id) = $sth->fetchrow_array()) {
1297 push @alt_ids, $alt_id ;
1299 return @alt_ids || undef ;
1302 =head2 map_to_slim
1304 Usage: my @match_list = $cvterm->map_to_slim(@term_list)
1305 Desc: returns a list of terms in the @term_list that
1306 are parents of the term object. This function is
1307 useful for mapping terms to a slim vocabulary.
1308 Ret: a list of term identifiers
1309 Args: a list of term identifiers that are in the slim vocabulary
1310 Side Effects: accesses the database
1311 Note: the db name is stripped off if provided (GO:0003832 is
1312 given as 0003832)
1313 Example:
1315 =cut
1317 sub map_to_slim {
1318 my $self = shift;
1319 my @slim = @_;
1321 my %slim_counts = ();
1322 for (my $i=0; $i<@slim; $i++) {
1324 # strip db name off id
1326 $slim[$i]=~s/.*?(\d+).*/$1/;
1328 # make a unique list of slim terms
1330 $slim_counts{$slim[$i]}=0;
1332 $self->get_slim_counts(\%slim_counts);
1334 print Data::Dumper::Dumper(\%slim_counts);
1336 my @matches = ();
1337 foreach my $k (keys %slim_counts) {
1338 if ($slim_counts{$k}>0) { push @matches, $k; }
1340 return @matches;
1343 sub get_slim_counts {
1344 my $self = shift;
1345 my $slim_counts = shift;
1347 my $id = $self->identifier();
1349 if (exists($slim_counts->{$id}) && defined($slim_counts->{$id})) {
1350 $slim_counts->{$id}++;
1351 return;
1354 foreach my $p ($self->get_parents()) {
1355 $p->[0]->get_slim_counts($slim_counts);
1360 # sub get_slim_counts {
1361 # my $self = shift;
1362 # my $slim_counts = shift;
1364 # foreach my $p ($self->get_parents()) {
1365 # my $id = $p->[0]->identifier();
1366 # $self->d("Checking $id\n");
1367 # if (exists($slim_counts->{$id}) && defined($slim_counts->{$id})) {
1368 # $slim_counts->{$id}++;
1370 # else {
1371 # $p->[0]->get_slim_counts($slim_counts);
1376 # return $slim_counts;
1379 # # sub get_slim_term {
1380 # my $self = shift;
1381 # my $slim_counts = shift;
1383 # my $slim = "";
1384 # foreach my $p ($self->get_parents()) {
1385 # my $id = $p->[0]->identifier();
1386 # if (exists($slim_counts->{$id}) && defined($slim_counts->{$id})) {
1387 # $slim = $id;
1388 # last();
1391 # return $slim;
1396 =head2 get_all_populations_cvterm
1398 Usage: my @pops = $cvterm->get_all_populations_cvterm();
1399 Desc: returns a list of populations phenotyped for the particular trait (cvterm).
1400 Ret: list of population objects
1401 Args: none
1402 Side Effects: accesses database
1403 Example:
1405 =cut
1407 sub get_all_populations_cvterm {
1408 my $self=shift;
1409 my $query = "SELECT DISTINCT(phenome.population.population_id) FROM public.phenotype
1411 LEFT JOIN phenome.individual USING (individual_id)
1412 LEFT JOIN phenome.population USING (population_id)
1413 WHERE observable_id = ?";
1414 my $sth=$self->get_dbh->prepare($query);
1415 $sth->execute($self->get_cvterm_id());
1416 my @populations;
1417 while (my ($pop_id) = $sth->fetchrow_array()) {
1418 my $pop = CXGN::Phenome::Population->new($self->get_dbh(), $pop_id);
1419 push @populations, $pop;
1421 return @populations;
1427 =head2 get_individuals
1429 Usage: $self->get_individuals
1430 Desc: find all individuals annotated with this cvterm
1431 Ret: list of Individual objects
1432 Args: none
1433 Side Effects: none
1434 Example:
1436 =cut
1438 sub get_individuals {
1439 my $self=shift;
1440 my $query = "SELECT individual_id FROM phenome.individual
1441 JOIN phenome.individual_dbxref USING (individual_id)
1442 WHERE dbxref_id=?";
1443 my $sth=$self->get_dbh()->prepare($query);;
1444 $sth->execute($self->get_dbxref_id);
1445 my @individuals;
1446 while ( my ($individual_id) = $sth->fetchrow_array() ) {
1447 my $ind= CXGN::Phenome::Individual->new($self->get_dbh(), $individual_id);
1448 push @individuals, $ind;
1450 return @individuals;
1453 =head2 get_loci
1455 Usage: $self->get_loci()
1456 Desc: find the loci with annotations of this cvterm
1457 Ret: a list of Locus objects
1458 Args: none
1459 Side Effects: none
1460 Example:
1462 =cut
1464 sub get_loci {
1465 my $self=shift;
1466 my $query = "SELECT locus_id FROM phenome.locus
1467 JOIN phenome.locus_dbxref USING (locus_id)
1468 WHERE dbxref_id=?
1469 ORDER BY locus.locus_symbol";
1470 my $sth=$self->get_dbh()->prepare($query);;
1471 $sth->execute($self->get_dbxref_id);
1472 my @loci;
1473 while ( my ($locus_id) = $sth->fetchrow_array() ) {
1474 my $locus= CXGN::Phenome::Locus->new($self->get_dbh(), $locus_id);
1475 push @loci, $locus;
1477 return @loci;
1480 =head2 get_recursive_loci
1482 Usage: my @loci= $self->get_recursive_loci()
1483 Desc: find all the loci annotated with the cvterm or any of its recursive children
1484 Ret: a list of locus ojects
1485 Args: none
1486 Side Effects: none
1487 Example:
1489 =cut
1491 sub get_recursive_loci {
1492 my $self=shift;
1493 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";
1495 my $sth=$self->get_dbh()->prepare($query);
1496 my @loci;
1497 $sth->execute($self->get_cvterm_id() );
1498 while ( my ($locus_id) = $sth->fetchrow_array() ) {
1499 my $locus= CXGN::Phenome::Locus->new($self->get_dbh(), $locus_id);
1500 push @loci, $locus;
1503 return @loci;
1506 =head2 get_recursive_individuals
1508 Usage: my @ind= $self->get_recursive_individuals()
1509 Desc: find all the individuals annotated with the cvterm or any of its recursive children
1510 Ret: a list of Individual ojects
1511 Args: none
1512 Side Effects: none
1513 Example:
1515 =cut
1517 sub get_recursive_individuals {
1518 my $self=shift;
1520 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 ";
1523 my $sth=$self->get_dbh()->prepare($query);;
1524 my @ind;
1525 $sth->execute($self->get_cvterm_id() );
1526 while ( my ($individual_id) = $sth->fetchrow_array() ) {
1527 my $individual= CXGN::Phenome::Individual->new($self->get_dbh(), $individual_id);
1528 push @ind, $individual;
1530 return @ind;
1538 1;#do not remove