start fixing test for multi cat phenotype upload.
[sgn.git] / lib / CXGN / Trait.pm
blob1d99028299a3ac16b817da3a405e57ce63293d99
2 package CXGN::Trait;
4 use Moose;
5 use Data::Dumper;
6 use Try::Tiny;
7 use JSON;
8 use CXGN::BrAPI::v2::ExternalReferences;
9 use CXGN::BrAPI::v2::Methods;
10 use CXGN::BrAPI::v2::Scales;
11 use CXGN::BrAPI::Exceptions::ConflictException;
12 use CXGN::BrAPI::Exceptions::ServerException;
14 ## to do: add concept of trait short name; provide alternate constructors for term, shortname, and synonyms etc.
16 has 'bcs_schema' => ( isa => 'Bio::Chado::Schema',
17 is => 'rw',
18 required => 1,
21 has 'cvterm_id' => (isa => 'Maybe[Int]',
22 is => 'rw',
23 #required => 1,
26 has 'cvterm' => ( isa => 'Bio::Chado::Schema::Result::Cv::Cvterm',
27 is => 'rw');
30 has 'name' => ( isa => 'Str',
31 is => 'rw',
32 lazy => 1,
33 default => sub {
34 my $self = shift;
35 return $self->cvterm->name();
40 has 'display_name' => (isa => 'Str',
41 is => 'ro',
42 lazy => 1,
43 default => sub {
44 my $self = shift;
45 my $db = $self->db();
46 my $name = $self->name();
47 my $accession = $self->accession();
48 #print STDERR $db." ".$name." ".$accession."\n";
49 if ($db && $name && $accession ) {
50 return $name ."|".$db.":".$accession;
52 return "";
56 has 'accession' => (isa => 'Str',
57 is => 'rw',
58 lazy => 1,
59 default => sub {
60 my $self = shift;
61 my $rs = $self->bcs_schema()->resultset("Cv::Cvterm")
62 -> search( { cvterm_id => $self->cvterm_id() })
63 -> search_related("dbxref");
64 if ($rs->count() ==1) {
65 my $accession = $rs->first()->get_column("accession");
66 return $accession;
68 return "";
73 has 'term' => (isa => 'Str',
74 is => 'ro',
75 lazy => 1,
76 default => sub {
77 my $self = shift;
78 my $accession = $self->accession();
79 my $db = $self->db();
80 if ($accession && $db) {
81 return "$db:$accession";
83 return "";
87 has 'db' => ( isa => 'Str',
88 is => 'rw',
89 lazy => 1,
90 default => sub {
91 my $self = shift;
92 my $rs = $self->bcs_schema()->resultset("Cv::Cvterm")->search( { cvterm_id => $self->cvterm_id()})->search_related("dbxref")->search_related("db");
93 if ($rs->count() == 1) {
94 my $db_name = $rs->first()->get_column("name");
95 #print STDERR "DBNAME = $db_name\n";
96 return $db_name;
98 return "";
103 has 'db_id' => (
104 isa => 'Int',
105 is => 'rw',
106 lazy => 1,
107 default => sub {
108 my $self = shift;
109 if ($self->cvterm){
110 my $rs = $self->cvterm->search_related("dbxref");
111 if ($rs->count() == 1) {
112 my $db_id = $rs->first()->get_column("db_id");
113 #print STDERR "DBID = $db_id\n";
114 return $db_id;
117 return "";
121 has 'dbxref_id' => (
122 isa => 'Int',
123 is => 'rw',
124 lazy => 1,
125 default => sub {
126 my $self = shift;
127 if ($self->cvterm){
128 my $rs = $self->cvterm->search_related("dbxref");
129 if ($rs->count() == 1) {
130 my $dbxref_id = $rs->first()->get_column("dbxref_id");
131 return $dbxref_id;
134 return "";
138 has 'definition' => (isa => 'Maybe[Str]',
139 is => 'rw',
140 lazy => 1,
141 default => sub {
142 my $self = shift;
143 return $self->cvterm->definition();
148 has 'entity' => (
149 isa => 'Maybe[Str]',
150 is => 'rw',
151 lazy => 1,
152 default => sub {
153 my $self = shift;
154 my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find(
155 { cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_entity' },
156 { join => 'type'}
159 if ($row) {
160 return $row->value();
162 return "";
166 has 'attribute' => (
167 isa => 'Maybe[Str]',
168 is => 'rw',
169 lazy => 1,
170 default => sub {
171 my $self = shift;
172 my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find(
173 { cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_attribute' },
174 { join => 'type'}
177 if ($row) {
178 return $row->value();
180 return "";
184 has 'format' => (isa => 'Str',
185 is => 'ro',
186 lazy => 1,
187 default => sub {
188 my $self = shift;
190 my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find(
192 cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_format'
194 { join => 'type'}
197 if ($row) {
198 return $row->value();
200 return "";
204 has 'default_value' => (
205 isa => 'Str',
206 is => 'ro',
207 lazy => 1,
208 default => sub {
209 my $self = shift;
210 my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find(
211 { cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_default_value' },
212 { join => 'type'}
215 if ($row) {
216 return $row->value();
218 return "";
222 has 'minimum' => (
223 isa => 'Str',
224 is => 'ro',
225 lazy => 1,
226 default => sub {
227 my $self = shift;
228 my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find(
229 { cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_minimum' },
230 { join => 'type'}
233 if ($row) {
234 return $row->value();
236 return "";
240 has 'maximum' => (
241 isa => 'Str',
242 is => 'ro',
243 lazy => 1,
244 default => sub {
245 my $self = shift;
246 my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find(
247 { cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_maximum' },
248 { join => 'type'}
251 if ($row) {
252 return $row->value();
254 return "";
258 has 'categories' => (
259 isa => 'Str',
260 is => 'ro',
261 lazy => 1,
262 default => sub {
263 my $self = shift;
264 my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find(
265 { cvterm_id => $self->cvterm_id(), 'type.name' => 'trait_categories' },
266 { join => 'type'}
269 if ($row) {
270 return $row->value();
272 return "";
276 has 'associated_plots' => ( isa => 'Str',
277 is => 'ro',
278 lazy => 1,
279 default => sub { "not yet implemented" }
282 has 'associated_accessions' => ( isa => 'Str',
283 is => 'ro',
284 lazy => 1,
285 default => sub { "not yet implemented" }
288 has 'uri' => (isa => 'Str',
289 is => 'ro',
290 lazy => 1,
291 default => sub {
292 my $self = shift;
293 my $row = $self->bcs_schema()->resultset("Cv::Cvtermprop")->find(
294 {cvterm_id => $self->cvterm_id(), 'type.name' => 'uri'},
295 { join => 'type'}
298 if ($row) {
299 return $row->value();
301 return "";
305 has 'ontology_id' => (
306 isa => 'Maybe[Int]',
307 is => 'rw',
310 has 'synonyms' => (
311 isa => 'Maybe[ArrayRef[Str]]',
312 is => 'rw',
313 lazy => 1,
314 default => sub {
315 my $self = shift;
316 my @synonyms = $self -> _fetch_synonyms();
317 return [@synonyms];
321 has 'external_references' => (
322 isa => 'Maybe[CXGN::BrAPI::v2::ExternalReferences]',
323 is => 'rw',
326 has 'method' => (
327 isa => 'Maybe[CXGN::BrAPI::v2::Methods]',
328 is => 'rw',
329 lazy => 1,
330 default => sub {
331 my $self = shift;
332 if (defined($self->cvterm_id)) {
333 return CXGN::BrAPI::v2::Methods->new({
334 bcs_schema => $self->bcs_schema,
335 cvterm_id => $self->cvterm_id
337 } else { return undef;}
341 has 'scale' => (
342 isa => 'Maybe[CXGN::BrAPI::v2::Scales]',
343 is => 'rw',
344 lazy => 1,
345 default => sub {
346 my $self = shift;
347 if (defined($self->cvterm_id)) {
348 return CXGN::BrAPI::v2::Scales->new({
349 bcs_schema => $self->bcs_schema,
350 cvterm_id => $self->cvterm_id
352 } else {return undef;}
356 has 'active' => (
357 isa => 'Bool',
358 is => 'rw'
361 has 'additional_info' => (
362 is => 'rw',
363 isa => 'Maybe[HashRef]'
366 has 'cv_id' => (
367 isa => 'Int',
368 is => 'ro',
369 lazy => 1,
370 default => sub {
371 my $self = shift;
372 my $cv_id = $self->bcs_schema->resultset("Cv::Cv")->find(
374 name => 'trait_property'
376 { key => 'cv_c1' }
377 )->get_column('cv_id');
378 return $cv_id;
382 has 'trait_entity_id' => (
383 isa => 'Int',
384 is => 'ro',
385 lazy => 1,
386 default => sub {
387 my $self = shift;
388 my $trait_entity_id = $self->bcs_schema->resultset("Cv::Cvterm")->find(
390 name => 'trait_entity',
391 cv_id => $self->cv_id,
392 is_obsolete => 0
394 { key => 'cvterm_c1' }
395 )->get_column('cvterm_id');
396 return $trait_entity_id;
400 has 'trait_attribute_id' => (
401 isa => 'Int',
402 is => 'ro',
403 lazy => 1,
404 default => sub {
405 my $self = shift;
406 my $trait_attribute_id = $self->bcs_schema->resultset("Cv::Cvterm")->find(
408 name => 'trait_attribute',
409 cv_id => $self->cv_id,
410 is_obsolete => 0
412 { key => 'cvterm_c1' }
413 )->get_column('cvterm_id');
414 return $trait_attribute_id;
418 sub BUILD {
419 #print STDERR "BUILDING...\n";
420 my $self = shift;
421 my $cvterm;
423 if ($self->cvterm_id){
424 # TODO: Throw a good error if cvterm can't be found
425 $cvterm = $self->bcs_schema()->resultset("Cv::Cvterm")->find( { cvterm_id => $self->cvterm_id });
426 $self->cvterm($cvterm);
427 $self->active($cvterm->is_obsolete == 0);
429 if (defined $cvterm) {
430 $self->name($self->name || $cvterm->name );
433 return $self;
437 sub store {
438 my $self = shift;
439 my $schema = $self->bcs_schema();
440 my $error;
442 # new variable
443 my $name = _trim($self->name());
444 my $description = $self->definition();
445 my $ontology_id = $self->ontology_id(); # passed in value not used currently, uses config
446 my $synonyms = $self->synonyms();
447 my $active = $self->active();
448 my $additional_info = $self->additional_info();
449 my $trait_entity = $self->entity();
450 my $trait_attribute = $self->attribute();
453 # get cv_id from sgn_local.conf
454 my $context = SGN::Context->new;
455 my $cv_name = $context->get_conf('trait_ontology_cv_name');
456 my $cvterm_name = $context->get_conf('trait_ontology_cvterm_name');
457 my $ontology_name = $context->get_conf('trait_ontology_db_name');
459 # Get trait attributes cvterm ids
460 my $trait_entity_id = $self->trait_entity_id;
461 my $trait_attribute_id = $self->trait_attribute_id;
463 # get cv_id for cv_name
464 my $cv = $schema->resultset("Cv::Cv")->find(
466 name => $cv_name
468 { key => 'cv_c1' }
470 my $cv_id = $cv->get_column('cv_id');
472 # get cvterm_id for cvterm_name
473 my $cvterm = $schema->resultset("Cv::Cvterm")->find(
475 name => $cvterm_name,
476 cv_id => $cv_id,
477 is_obsolete => 0
479 { key => 'cvterm_c1' }
482 my $root_id = $cvterm->get_column('cvterm_id');
484 # check to see if specified ontology exists
485 my $db = $schema->resultset("General::Db")->find(
487 name => $ontology_name
489 { key => 'db_c1' }
492 if (!defined($db)) {
493 CXGN::BrAPI::Exceptions::ServerException->throw({message => "Error: Unable to create trait, ontology does not exist"});
496 $ontology_id = $db->get_column('db_id');
498 # check to see if cvterm name already exists and don't attempt if so
499 my $cvterm_exists = $schema->resultset("Cv::Cvterm")->find(
501 name => $name,
502 cv_id => $cv_id,
503 is_obsolete => 0
505 { key => 'cvterm_c1' }
508 if (defined($cvterm_exists)) {
509 CXGN::BrAPI::Exceptions::ConflictException->throw({message => "Variable with that name already exists"});
512 # lookup last numeric accession number in ontology if one exists so we can increment off that
513 my $q = "select accession from dbxref where db_id=".$ontology_id." and accession ~ ".q('^\d+$')." order by accession desc limit 1;";
514 my $sth = $self->bcs_schema->storage->dbh->prepare($q);
515 $sth->execute();
516 my ($accession) = $sth->fetchrow_array();
518 if (!defined($accession)) {
519 $accession = '0000001';
520 } else {
521 $accession++;
524 # get cvterm_id for VARIABLE_OF
525 my $variable_of_cvterm = $schema->resultset("Cv::Cvterm")->find(
527 name => 'VARIABLE_OF',
528 cv_id => $cv_id,
529 is_obsolete => 0
531 { key => 'cvterm_c1' }
534 my $variable_of_id = $variable_of_cvterm->get_column('cvterm_id');
536 my $new_term;
538 # setup transaction for rollbacks in case of error
539 my $coderef = sub {
541 # add trait info to dbxref
542 my $new_term_dbxref = $schema->resultset("General::Dbxref")->create(
543 { db_id => $ontology_id,
544 accession => $accession,
545 version => '1',
547 { key => 'dbxref_c1' },
550 # add trait info to cvterm
551 $new_term = $schema->resultset("Cv::Cvterm")->create(
552 { cv_id => $cv_id,
553 name => $name,
554 definition => $description,
555 dbxref_id => $new_term_dbxref->dbxref_id(),
556 is_obsolete => $active ? 0 : 1
559 # set cvtermrelationship VARIABLE_OF to put terms under ontology
560 # add cvterm_relationship entry linking term to ontology root
561 my $relationship = $schema->resultset("Cv::CvtermRelationship")->create(
562 { type_id => $variable_of_id,
563 subject_id => $new_term->get_column('cvterm_id'),
564 object_id => $root_id
567 # add synonyms
568 foreach my $synonym (@{$synonyms}) {
569 $new_term->add_synonym($synonym);
572 # Add trait entity
573 if ($trait_entity) {
574 my $prop_entity = $schema->resultset("Cv::Cvtermprop")->create(
576 cvterm_id => $new_term->get_column('cvterm_id'),
577 type_id => $trait_entity_id,
578 value => $trait_entity,
579 rank => 0
585 # Add trait attribute
586 if ($trait_attribute) {
587 my $prop_attribute = $schema->resultset("Cv::Cvtermprop")->create(
589 cvterm_id => $new_term->get_column('cvterm_id'),
590 type_id => $trait_attribute_id,
591 value => $trait_attribute,
592 rank => 0
597 # Save additional info
598 my $rank = 0;
599 my $additional_info_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'cvterm_additional_info', 'trait_property')->cvterm_id();
600 if (defined $additional_info) {
601 my $prop_id = $schema->resultset("Cv::Cvtermprop")->create(
603 cvterm_id => $new_term->get_column('cvterm_id'),
604 type_id => $additional_info_type_id,
605 value => encode_json $additional_info,
606 rank => $rank
611 # save scale properties
612 $self->scale->{cvterm_id} = $new_term->cvterm_id;
613 $self->scale->store();
614 $self->method->{cvterm_id} = $new_term->cvterm_id;
615 $self->method->store();
616 if ($self->external_references) {
617 $self->external_references->{id} = $new_term->cvterm_id;
618 my $result = $self->external_references->store();
619 $self->external_references->{id} = [$new_term->cvterm_id];
624 $self->bcs_schema()->txn_do($coderef);
626 $self->cvterm_id($new_term->get_column('cvterm_id'));
627 $self->cvterm($new_term);
629 return $self;
632 sub update {
634 my $self = shift;
635 my $schema = $self->bcs_schema();
637 # new variable
638 my $name = _trim($self->name());
639 my $description = $self->definition();
640 my $entity = $self->entity();
641 my $attribute = $self->attribute();
642 my $active = $self->active();
643 my $synonyms = $self->synonyms();
644 my $cvterm_id = $self->cvterm_id();
645 my $additional_info = $self->additional_info();
647 my $trait_entity_id = $self->trait_entity_id();
648 my $trait_attribute_id = $self->trait_attribute_id();
650 $self->bcs_schema()->txn_do(sub {
652 # Update the variable
653 $self->cvterm->update({
654 name => $name,
655 definition => $description,
656 is_obsolete => $active ? 0 : 1
659 # Remove old synonyms
660 $self->delete_existing_synonyms();
662 # Add new synonyms
663 foreach my $synonym (@{$synonyms}) {
664 $self->cvterm->add_synonym($synonym);
667 # Update trait entity
668 $schema->resultset("Cv::Cvtermprop")->search(
670 cvterm_id => $self->cvterm_id,
671 type_id => $trait_entity_id
673 )->delete;
674 if ($entity) {
675 $schema->resultset("Cv::Cvtermprop")->create(
677 cvterm_id => $self->cvterm_id,
678 type_id => $trait_entity_id,
679 value => $entity,
680 rank => 0
685 # Update trait attribute
686 $schema->resultset("Cv::Cvtermprop")->search(
688 cvterm_id => $self->cvterm_id,
689 type_id => $trait_attribute_id
691 )->delete;
692 if ($attribute) {
693 $schema->resultset("Cv::Cvtermprop")->create(
695 cvterm_id => $self->cvterm_id,
696 type_id => $trait_attribute_id,
697 value => $attribute,
698 rank => 0
703 # Delete old additional info
704 my $additional_info_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'cvterm_additional_info', 'trait_property')->cvterm_id();
705 $schema->resultset("Cv::Cvtermprop")->search(
707 cvterm_id => $self->cvterm_id,
708 type_id => $additional_info_type_id
710 )->delete;
712 # Add new additional info
713 my $rank = 0;
714 if (defined $additional_info) {
715 my $prop_id = $schema->resultset("Cv::Cvtermprop")->create(
717 cvterm_id => $self->cvterm_id,
718 type_id => $additional_info_type_id,
719 value => encode_json $additional_info,
720 rank => $rank
725 # update scale properties
726 $self->scale->{cvterm_id} = $cvterm_id;
727 $self->scale->store();
728 $self->method->{cvterm_id} = $cvterm_id;
729 $self->method->store();
730 if ($self->external_references) {
731 $self->external_references->{id} = $cvterm_id;
732 $self->external_references->store();
733 $self->external_references->{id} = [ $cvterm_id ];
737 # Get the variable
738 #TODO: Query the variable
739 return $self;
742 sub delete_existing_synonyms {
743 my $self = shift;
744 my $schema = $self->bcs_schema();
745 $schema->resultset("Cv::Cvtermsynonym")->search(
746 {cvterm_id => $self->cvterm_id}
747 )->delete;
750 sub _fetch_synonyms {
751 my $self = shift;
752 my @synonyms = ();
753 if (defined($self->cvterm)){
754 my $synonym_rs = $self->cvterm->cvtermsynonyms;
756 while ( my $s = $synonym_rs->next ) {
757 push @synonyms, $s->synonym;
760 my @sorted = sort { length $a <=> length $b } @synonyms;
761 return @sorted;
764 # TODO: common utilities somewhere, used by Location also
765 sub _trim { #trim whitespace from both ends of a string
766 my $s = shift;
767 $s =~ s/^\s+|\s+$//g;
768 return $s;
771 # gmod
772 sub numeric_id {
773 my $id = shift;
774 $id =~ s/.*\:(.*)$/$1/g;
775 return $id;
778 sub get_active_string {
779 my $self = shift;
780 return $self->{active} ? 'active' : 'archived';