#$Id: OntologyServer.pm,v 1.3 2008/09/02 13:14:18 kawas Exp $ # this module needs to talk to the 'real' ontology # server as well as the MOBY Central database # in order to ensure that they are both in sync =head1 NAME MOBY::OntologyServer - A way for MOBY Central to query the object, service, namespace, and relationship ontologies =cut =head1 SYNOPSIS use MOBY::OntologyServer; my $OS = MOBY::OntologyServer->new(ontology => "object"); my ($success, $message, $existingURI) = $OS->objectExists(term => "Object"); if ($success){ print "object exists and it has the LSID $existingURI\n"; } else { print "object does not exist; additional message from server: $message\n"; } =cut =head1 DESCRIPTION Swappable interface to ontologies. It should deal with LSID's 100% of the time, and also deal with MOBY-specific common names for objects, services, namespaces, and relationship types. =head1 AUTHORS Mark Wilkinson (markw@illuminae.com) BioMOBY Project: http://www.biomoby.org =cut =head1 METHODS =head2 new Title : new Usage : my $OS = MOBY::OntologyServer->new(%args) Function : Returns : MOBY::OntologyServer object Args : ontology => [object || service || namespace || relationship] database => mysql databasename that holds the ontologies host => mysql hostname username => mysql username password => mysql password port => mysql port dbh => pre-existing database handle to a mysql database =cut package MOBY::OntologyServer; use strict; use Carp; use vars qw($AUTOLOAD); use DBI; use DBD::mysql; use MOBY::Config; use vars qw /$VERSION/; $VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /: (\d+)\.(\d+)/; my $debug = 0; { #Encapsulated class data #___________________________________________________________ #ATTRIBUTES my %_attr_data = # DEFAULT ACCESSIBILITY ( ontology => [ undef, 'read/write' ], database => [ undef, 'read/write' ], host => [ undef, 'read/write' ], username => [ undef, 'read/write' ], password => [ undef, 'read/write' ], port => [ undef, 'read/write' ], dbh => [ undef, 'read/write' ], ); #_____________________________________________________________ # METHODS, to operate on encapsulated class data # Is a specified object attribute accessible in a given mode sub _accessible { my ( $self, $attr, $mode ) = @_; $_attr_data{$attr}[1] =~ /$mode/; } # Classwide default value for a specified object attribute sub _default_for { my ( $self, $attr ) = @_; $_attr_data{$attr}[0]; } # List of names of all specified object attributes sub _standard_keys { keys %_attr_data; } } sub new { my ( $caller, %args ) = @_; my $caller_is_obj = ref($caller); my $class = $caller_is_obj || $caller; my $self = bless {}, $class; foreach my $attrname ( $self->_standard_keys ) { if ( exists $args{$attrname} && defined $args{$attrname} ) { $self->{$attrname} = $args{$attrname}; } elsif ($caller_is_obj) { $self->{$attrname} = $caller->{$attrname}; } else { $self->{$attrname} = $self->_default_for($attrname); } } $self->ontology eq 'object' && $self->database('mobyobject'); $self->ontology eq 'namespace' && $self->database('mobynamespace'); $self->ontology eq 'service' && $self->database('mobyservice'); $self->ontology eq 'relationship' && $self->database('mobyrelationship'); #print STDERR "\n\nCONFIG object is $CONFIG\n\n"; $CONFIG ||= MOBY::Config->new; #print STDERR "got username ",($CONFIG->{mobycentral}->{username})," for mobycentral\n"; $self->username( $CONFIG->{ $self->database }->{username} ) unless $self->username; $self->password( $CONFIG->{ $self->database }->{password} ) unless $self->password; $self->port( $CONFIG->{ $self->database }->{port} ) unless $self->port; $self->host( $CONFIG->{ $self->database }->{url} ) unless $self->host; my $host = $self->host ? $self->host : $ENV{MOBY_CENTRAL_URL}; chomp $host; my $username = $self->username ? $self->username : $ENV{MOBY_CENTRAL_DBUSER}; chomp $username; my $password = $self->password ? $self->password : $ENV{MOBY_CENTRAL_DBPASS}; chomp $password if $password; $password =~ s/\s//g if $password; my $port = $self->port ? $self->port : $ENV{MOBY_CENTRAL_DBPORT}; chomp $port; my ($dsn) = "DBI:mysql:" . ( $CONFIG->{ $self->database }->{dbname} ) . ":" . ($host) . ":" . ($port); #print STDERR "\n\nDSN was $dsn\n\n"; my $dbh; # $debug && &_LOG("connecting to db with params ",$self->database, $self->username, $self->password,"\n"); if ( defined $password ) { $dbh = DBI->connect( $dsn, $username, $password, { RaiseError => 1 } ) or die "can't connect to database"; } else { $dbh = DBI->connect( $dsn, $username, undef, { RaiseError => 1 } ) or die "can't connect to database"; } # $debug && &_LOG("CONNECTED!\n"); if ($dbh) { $self->dbh($dbh); return $self; } else { return undef; } } =head2 objectExists moby:newterm will return (0, $message, $MOBYLSID) newterm will return (0, $message, $MOBYLSID oldterm will return (1, $message, undef) newLSID will return (0, $desc, $lsid) =cut sub objectExists { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); my $term = $args{term}; $term =~ s/^moby://; # if the term is namespaced, then remove that my $sth; return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'object' ); return (0, undef, undef) unless $term; my $result; $result = $adaptor->query_object(type => $term); my $row = shift(@$result); my $lsid = $row->{object_lsid}; my $type = $row->{object_type}; my $desc = $row->{description}; my $auth = $row->{authority}; my $email = $row->{contact_email}; if ($lsid) { # if it is in there, then it has been discovered regardless of being foreign or not return ( 1, $desc, $lsid ); } elsif ( _isForeignLSID($term) ) { # if not in our ontology, but is a foreign LSID, then pass it back verbatim return ( 0, "LSID $term does not exist in the biomoby.org Object Class system\n", $term ); } else { # under all other circumstances (i.e. not a term, or a non-existent biomoby LSID) then fail return ( 0, "Object type $term does not exist in the biomoby.org Object Class system\n", '' ); } } =head2 objectInfo =cut sub objectInfo{ my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); my $term = $args{term}; $term =~ s/^moby://; # if the term is namespaced, then remove that my $sth; return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'object' ); return (0, undef, undef) unless $term; my $result; $result = $adaptor->query_object(type => $term); my $row = shift(@$result); #my $lsid = $row->{object_lsid}; #my $type = $row->{object_type}; #my $desc = $row->{description}; #my $auth = $row->{authority}; #my $email = $row->{contact_email}; # if ($row->{object_lsid}) { # if it is in there, then it has been discovered regardless of being foreign or not return $row; } elsif ( _isForeignLSID($term) ) { # if not in our ontology, but is a foreign LSID, then pass it back verbatim return {object_lsid => $term, object_type => $term, description => "LSID $term does not exist in the biomoby.org Object Class system\n", authority => "", contact_email => "", }; } else { # under all other circumstances (i.e. not a term, or a non-existent biomoby LSID) then fail return {object_lsid => "", object_type => "", description => "LSID $term does not exist in the biomoby.org Object Class system\n", authority => "", contact_email => "", }; } } =head2 serviceInfo =cut sub serviceInfo{ my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyservice' ); my $term = $args{term}; $term =~ s/^moby://; # if the term is namespaced, then remove that my $sth; return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'service' ); return (0, undef, undef) unless $term; my $result; $result = $adaptor->query_service(type => $term); my $row = shift(@$result); if ($row->{service_lsid}) { # if it is in there, then it has been discovered regardless of being foreign or not return $row; } elsif ( _isForeignLSID($term) ) { # if not in our ontology, but is a foreign LSID, then pass it back verbatim return {service_lsid => $term, service_type => $term, description => "LSID $term does not exist in the biomoby.org Object Class system\n", authority => "", contact_email => "", }; } else { # under all other circumstances (i.e. not a term, or a non-existent biomoby LSID) then fail return {service_lsid => "", service_type => "", description => "LSID $term does not exist in the biomoby.org Object Class system\n", authority => "", contact_email => "", }; } } sub _isMOBYLSID { my ($lsid) = @_; return 1 if $lsid =~ /^urn\:lsid\:biomoby.org/; return 0; } sub _isForeignLSID { my ($lsid) = @_; return 0 if $lsid =~ /^urn\:lsid\:biomoby.org/; return 1; } =head2 createObject =cut sub createObject { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'object' ); return ( 0, "requires a object type node", '' ) unless ( $args{node} ); return ( 0, "requires an authURI ", '' ) unless ( $args{authority} ); return ( 0, "requires a contact email address", '' ) unless ( $args{contact_email} ); return ( 0, "requires a object description", '' ) unless ( $args{description} ); my $term = $args{node}; my $result; $result = $adaptor->query_object(type => $term); my $row = shift(@$result); my $lsid = $row->{object_lsid}; my $type = $row->{object_type}; my $desc = $row->{description}; my $auth = $row->{authority}; my $email = $row->{contact_email}; if ($lsid) { # if it is in there, then the object exists return ( 0, "This term already exists: $lsid", $lsid ); } my $LSID = $self->setURI( $term ); unless ($LSID) { return ( 0, "Failed during creation of an LSID", '' ) } $args{description} =~ s/^\s+(.*?)\s+$/$1/s; $args{node} =~ s/^\s+(.*?)\s+$/$1/s; $args{contact_email} =~ s/^\s+(.*?)\s+$/$1/s; $args{authority} =~ s/^\s+(.*?)\s+$/$1/s; my $insertid = $adaptor->insert_object(object_type => $args{'node'}, object_lsid => $LSID, description => $args{'description'}, authority => $args{'authority'}, contact_email => $args{'contact_email'}); unless ( $insertid ) { return ( 0, "Object creation failed for unknown reasons", '' ); } return ( 1, "Object creation succeeded", $LSID ); } =head2 retrieveObject =cut sub retrieveObject { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); my $term = $args{'type'}; $term ||=$args{'node'}; return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'object' ); return ( 0, "requires a object type node as an argument", '' ) unless ( $term ); my $LSID = ( $term =~ /urn\:lsid/ ) ? $term : $self->getObjectURI($term); unless ($LSID) { return ( 0, "Failed during creation of an LSID", '' ) } my $result = $adaptor->query_object(type => $LSID); my $row = shift(@$result); my $type = $row->{object_type}; my $lsid = $row->{object_lsid}; my $desc = $row->{description}; my $auth = $row->{authority}; my $contact = $row->{contact_email}; unless ($lsid) { return ( 0, "Object doesn't exist in ontology", "" ) } $result = $adaptor->get_object_relationships(type => $lsid); my %rel; foreach my $row (@$result) { my $relationship_type = $row->{relationship_type}; my $objectlsid = $row->{object_lsid}; my $article = $row->{object2_articlename}; my $contact = $row->{contact_email}; my $def = $row->{definition}; my $auth = $row->{authority}; my $type = $row->{object_type}; push @{ $rel{$relationship_type} }, [ $objectlsid, $article, $type, $def, $auth, $contact ]; } return { objectType => $type, objectLSID => $lsid, description => $desc, contactEmail => $contact, authURI => $auth, Relationships => \%rel }; } =head2 deprecateObject =cut sub deprecateObject { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); return ( 0, "WRONG ONTOLOGY", '' ) unless ( $self->ontology eq 'object' ); my $term = $args{term}; # if ($term =~ /^urn:lsid/ && !($term =~ /^urn:lsid:biomoby.org:objectclass/)){ # return (0, "can't delete from external ontology", $term); # } my $LSID; unless ( $term =~ /urn\:lsid/ ) { $LSID = $self->getObjectURI($term) } else { $LSID = $term } return ( 0, "Object type $term cannot be resolved to an LSID", "" ) unless $LSID; my $result = $adaptor->query_object(type => $LSID); my $row = shift(@$result); my $id = $row->{object_id}; my $lsid = $row->{object_lsid}; # object1_id ISA object2_id? my $isa = $adaptor->query_object_term2term(type => $lsid); my $isas = shift @$isa; if ( $isas->{object1_id}) { return ( 0, qq{Object type $term has object dependencies in the ontology}, $lsid ); } my ($err, $errstr) = $adaptor->delete_object(type => $lsid); if ( $err ) { return ( 0, "Delete from Object Class table failed: $errstr", $lsid ); } return ( 1, "Object $term Deleted", $lsid ); } =head2 deleteObject =cut sub deleteObject { my $self = shift; $self->deprecateObject(@_); } =head2 relationshipExists =cut sub relationshipExists { # term => $term # ontology => $ontology my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyrelationship' ); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'relationship' ); my $term = lc( $args{term} ); $term =~ s/^moby://; # if the term is namespaced, then remove that my $ont = $args{ontology}; return ( 0, "requires both term and ontology arguments\n", '' ) unless ( defined($term) && defined($ont) ); my $result; if ( $term =~ /^urn\:lsid/ ) { $result = $adaptor->query_relationship( type => $term, ontology => $ont); } else { $result = $adaptor->query_relationship(type => $term, ontology => $ont); } my $row = shift(@$result); my $lsid = $row->{relationship_lsid}; my $type = $row->{relationship_type}; my $desc = $row->{description}; my $auth = $row->{authority}; my $email = $row->{contact_email}; if ($lsid) { return ( 1, $desc, $lsid, $type, $auth, $email ); } else { return ( 0,"Relationship Type $term does not exist in the biomoby.org Relationship Type system\n", '', '', '', '' ); } } =head2 addObjectRelationship =cut sub addObjectRelationship { # adds a relationship #subject_node => $term, #relationship => $reltype, #object_node => $objectType, #articleName => $articleName, #authority => $auth, #contact_email => $email my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'object' ); my $result = $adaptor->query_object(type => $args{subject_node}); my $row = shift(@$result); my $subj_lsid = $row->{object_lsid}; return ( 0, qq{Object type $args{subject_node} does not exist in the ontology}, '' ) unless defined $subj_lsid; $result = $adaptor->query_object(type => $args{object_node}); $row = shift(@$result); my $obj_lsid = $row->{object_lsid}; return ( 0,qq{Object type $args{object_node} does not exist in the ontology},'' ) unless defined $obj_lsid; my $isa = $adaptor->query_object_term2term(type => $subj_lsid); my $isarow = shift @$isa; if ( $isarow->{object_lsid} ) { return ( 0, qq{Object type $args{subject_node} has existing object dependencies in the ontology. It cannot be changed.}, $subj_lsid ); } my $OE = MOBY::OntologyServer->new( ontology => 'relationship' ); my ( $success, $desc, $rel_lsid ) = $OE->relationshipExists( term => $args{relationship}, ontology => 'object' ); ($success) || return ( 0, qq{Relationship $args{relationship} does not exist in the ontology}, '' ); # need to ensure that identical article names dont' end up at the same level my $articleNameInvalid = &_testIdenticalArticleName(term => $subj_lsid, articleName => $args{articleName}); return (0, "Object will have conflicting articleName ".($args{articleName}), '') if $articleNameInvalid; my $insertid = $adaptor->insert_object_term2term(relationship_type => $rel_lsid, object1_type => $subj_lsid, object2_type => $obj_lsid, object2_articlename => $args{articleName}); if ($insertid ) { return ( 1, "Object relationsihp created successfully", '' ); } else { return ( 0, "Object relationship creation failed for unknown reasons", '' ); } } sub _testIdenticalArticleName { my (%args)= @_; my $term = $args{term}; my $articleName = $args{articleName}; my $foundCommonArticleNameFlag = 0; # need to first traverse down the ISA pathway to root # then for each ISA test the hAS and HASA's for their articlenames and see if they are the same # case insensitive? my $OS = MOBY::OntologyServer->new(ontology => 'object'); my $OSrel = MOBY::OntologyServer->new(ontology => 'relationship'); my ($exists1, $desc, $isalsid) = $OSrel->relationshipExists(term => 'isa', ontology => 'object'); my ($exists2, $desc2, $hasalsid) = $OSrel->relationshipExists(term => 'hasa', ontology => 'object'); my ($exists3, $desc3, $haslsid) = $OSrel->relationshipExists(term => 'has', ontology => 'object'); return 1 unless ($exists1 && $exists2 && $exists3); # this is bad, since it returns boolean suggesting that it found a common articlename rather than finding that a given relationship doesn't exist, but... hey.... # check the hasa relationships for common articleName $foundCommonArticleNameFlag += _compareArticleNames(OS => $OS, type => $args{term}, relationship => $hasalsid, targetArticleName => $articleName); # check the has relationships for common articleName $foundCommonArticleNameFlag += _compareArticleNames(OS => $OS, type => $args{term}, relationship => $haslsid, targetArticleName => $articleName); # now get all of its inherited parents my $relationships = $OS->Relationships( ontology => 'object', term => $args{term}, relationship => $isalsid, direction => 'root', expand => 1); #relationships{relationship} = [[lsid1,articleNmae], [lsid2, articleName], [lsid3, articleName]] my ($isa) = keys(%$relationships); # can only be one key returned, and must be isa in this case my @ISAlist; (@ISAlist = @{$relationships->{$isa}}) if ($relationships->{$isa}) ; # for each of the inherited parents, check their articleNames foreach my $ISA(@ISAlist){ # $ISA = [lsid, articleName] (but articleName shuld be null anyway in this case) my $what_it_is = $ISA->{lsid}; # check the hasa relationships for common articleName $foundCommonArticleNameFlag += _compareArticleNames(OS => $OS, type => $what_it_is, relationship => $hasalsid, targetArticleName => $articleName); # check the has relationships for common articleName $foundCommonArticleNameFlag += _compareArticleNames(OS => $OS, type => $what_it_is, relationship => $haslsid, targetArticleName => $articleName); } return $foundCommonArticleNameFlag; } sub _compareArticleNames { my (%args) = @_; my $OS = $args{OS}; my $what_it_is = $args{type}; my $lsid = $args{relationship}; my $targetArticleName = $args{targetArticleName}; my $foundCommonArticleNameFlag = 0; my $contents = $OS->Relationships( ontology => 'object', term => $what_it_is, relationship => $lsid, direction => 'root', ); if ($contents){ #$hasarelationships{relationship} = [[lsid1,articleNmae], [lsid2, articleName], [lsid3, articleName]] my ($content) = keys(%$contents); if ($contents->{$content}){ my @CONTENTlist = @{$contents->{$content}}; foreach my $CONTAINED(@CONTENTlist){ $foundCommonArticleNameFlag = 1 if ($CONTAINED->{articleName} eq $targetArticleName); #->[1] is the articleName field } } } return $foundCommonArticleNameFlag; } =head2 addServiceRelationship =cut sub addServiceRelationship { # adds an ISA relationship # fail if another object is in relation to this objevt #subject_node => $term, #relationship => $relationship, #predicate_node => $pred #authority => $auth, #contact_email => $email); my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyservice' ); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'service' ); my $result = $adaptor->query_service(type => $args{subject_node}); my $row = shift(@$result); my $sbj_lsid = $row->{service_lsid}; return (0, qq{Service type $args{subject_node} has object dependencies in the ontology. It can not be changed}, $sbj_lsid ) unless defined $sbj_lsid; my $isa = $adaptor->query_service_term2term(service2_id => $sbj_lsid); my $isarow = shift @$isa; if ( $isarow->{service_lsid} ) { return ( 0, qq{Service type $args{subject_node} has object dependencies in the ontology. It can not be changed}, $sbj_lsid ); } $result = $adaptor->query_service(type => $args{object_node}); $row = shift(@$result); my $obj_lsid = $row->{service_lsid}; # get ID of the related service defined $obj_lsid || return ( 0, qq{Service $args{object_node} does not exist in the service ontology}, '' ); my $OE = MOBY::OntologyServer->new( ontology => 'relationship' ); my ( $success, $desc, $rel_lsid ) = $OE->relationshipExists( term => $args{relationship}, ontology => 'service' ); ($success) || return ( 0, qq{Relationship $args{relationship} does not exist in the ontology}, '' ); my $insertid = $adaptor->insert_service_term2term(relationship_type => $rel_lsid, service1_type => $sbj_lsid, service2_type => $obj_lsid); if ( defined($insertid)) { return ( 1, "Service relationship created successfully", '' ); } else { return ( 0, "Service relationship creation failed for unknown reasons", '' ); } } =head2 serviceExists =cut sub serviceExists { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyservice' ); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'service' ); my $term = $args{term}; $term =~ s/^moby://; # if the term is namespaced, then remove that if ( $term =~ /^urn:lsid/ && !( $term =~ /^urn:lsid:biomoby.org:servicetype/ ) ) { return ( 1, "external ontology", $term ); } return (0, undef, undef) unless $term; my $result; $result = $adaptor->query_service(type => $term); my $row = shift(@$result); my $id = $row->{service_id}; my $type = $row->{service_type}; my $lsid = $row->{service_lsid}; my $desc = $row->{description}; my $auth = $row->{authority}; my $email = $row->{contact_email}; if ($id) { return ( 1, $desc, $lsid ); } else { return ( 0, "Service Type $term does not exist in the biomoby.org Service Type ontology\n", '' ); } } =head2 createServiceType =cut sub createServiceType { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyservice' ); #node => $term, #descrioption => $desc, #authority => $auth, #contact_email => $email); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'service' ); return ( 0, "requires a object type node", '' ) unless ( $args{node} ); return ( 0, "requires an authURI ", '' ) unless ( $args{authority} ); return ( 0, "requires a contact email address", '' ) unless ( $args{contact_email} ); return ( 0, "requires a object description", '' ) unless ( $args{description} ); my $term = $args{node}; if ( $term =~ /^urn:lsid/ && !( $term =~ /^urn:lsid:biomoby.org:servicetype/ ) ) { # if it is an LSID, but not a MOBY LSID, than barf return ( 0, "can't create a term in a non-MOBY ontology!", $term ); } my $LSID =$self->setURI( $args{'node'} ); unless ($LSID) { return ( 0, "Failed during creation of an LSID", '' ) } my $insertid = $adaptor->insert_service(service_type => $args{'node'}, service_lsid => $LSID, description => $args{'description'}, authority => $args{'authority'}, contact_email => $args{'contact_email'}); unless ( $insertid ) { return ( 0, "Service creation failed for unknown reasons", '' ); } return ( 1, "Service creation succeeded", $LSID ); } =head2 deleteServiceType =cut sub deleteServiceType { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyservice' ); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'service' ); my $term = $args{term}; if ( $term =~ /^urn:lsid/ && !( $term =~ /^urn:lsid:biomoby.org:servicetype/ ) ) { return ( 0, "can't delete from external ontology", $term ); } my $LSID; unless ( $term =~ /^urn:lsid:biomoby.org:servicetype/ ) { $LSID = $self->getServiceURI($term); } else { $LSID = $term; } return ( 0, q{Service type $term cannot be resolved to an LSID in the MOBY ontologies},"" ) unless $LSID; my $result = $adaptor->query_service(type => $LSID); my $row = shift(@$result); my $lsid = $row->{service_lsid}; if ( !defined $lsid ) { return ( 0, q{Service type $term does not exist in the ontology}, $lsid ); } # service1_id ISA service2_id? my $isa = $adaptor->query_service_term2term(type => $lsid); my $isas = shift(@$isa); if ( $isas->{service1_id} ) { return ( 0, qq{Service type $term has dependencies in the ontology}, $lsid ); } my ($err, $errstr) = $adaptor->delete_service(type => $lsid); if ( $err ) { return ( 0, "Delete from Service Type table failed: $errstr", $lsid ); } return ( 1, "Service Type $term Deleted", $lsid ); } =head2 namespaceExists =cut sub namespaceExists { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobynamespace' ); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'namespace' ); my $term = $args{term}; return (0, undef, undef) unless $term; $term =~ s/^moby://; # if the term is namespaced, then remove that if ( $term =~ /^urn:lsid/ && !( $term =~ /^urn:lsid:biomoby.org:namespacetype/ ) ) { return ( 1, "external ontology", $term ); } my $result; $result = $adaptor->query_namespace(type => $term); my $row = shift(@$result); my $id = $row->{namespace_id}; my $type = $row->{namespace_type}; my $lsid = $row->{namespace_lsid}; my $desc = $row->{description}; my $auth = $row->{authority}; my $email = $row->{contact_email}; if ($id) { return ( 1, $desc, $lsid ); } else { return ( 0, "Namespace Type $term does not exist in the biomoby.org Namespace Type ontology\n", '' ); } } =head2 createNamespace =cut sub createNamespace { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobynamespace' ); #node => $term, #descrioption => $desc, #authority => $auth, #contact_email => $email); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'namespace' ); return ( 0, "requires a namespace type node", '' ) unless ( $args{node} ); return ( 0, "requires an authURI ", '' ) unless ( $args{authority} ); return ( 0, "requires a contact email address", '' ) unless ( $args{contact_email} ); return ( 0, "requires a object description", '' ) unless ( $args{description} ); my $term = $args{node}; if ( $term =~ /^urn:lsid/){ # if it is an LSID, barf return ( 0, "can't create a term from an lsid!", $term ); } my $LSID = $self->setURI( $term ); unless ($LSID) { return ( 0, "Failed during creation of an LSID", '' ) } my $insertid = $adaptor->insert_namespace(namespace_type => $args{'node'}, namespace_lsid => $LSID, description => $args{'description'}, authority => $args{'authority'}, contact_email => $args{'contact_email'}); unless ( $insertid ) { return ( 0, "Namespace creation failed for unknown reasons", '' ); } return ( 1, "Namespace creation succeeded", $LSID ); } =head2 deleteNamespace =cut sub deleteNamespace { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobynamespace' ); return ( 0, "WRONG ONTOLOGY!", '' ) unless ( $self->ontology eq 'namespace' ); my $term = $args{term}; my $LSID; unless ( $term =~ /urn\:lsid/ ) { $LSID = $self->getNamespaceURI($term) } else { $LSID = $term } return ( 0, q{Namespace type $term cannot be resolved to an LSID}, "" ) unless $LSID; if ( $term =~ /^urn:lsid/ && !( $term =~ /^urn:lsid:biomoby.org:namespacetype/ ) ) { return ( 0, "cannot delete a term from an external ontology", $term ); } my $result = $adaptor->query_namespace(type => $LSID); my $row = shift(@$result); my $lsid = $row->{namespace_lsid}; unless ($lsid) { return ( 0, q{Namespace type $term does not exist in the ontology}, $lsid ); } # service1_id ISA service2_id? my $isa = $adaptor->query_namespace_term2term(type => $lsid); my $isas = shift @$isa; if ($isas->{namespace1_id} ) { return ( 0, qq{Namespace type $term has dependencies in the ontology}, $lsid ); } my ($err, $errstr) = $adaptor->delete_namespace(type => $lsid); if ( $err ) { return ( 0, "Delete from namespace table failed: $errstr", $lsid ); } #($err, $errstr) = $adaptor->delete_namespace_term2term(namespace1_id => $lsid); # #if ( $err ) { # return ( # 0, # "Delete from namespace term2term table failed: $errstr", # $lsid # ); #} return ( 1, "Namespace Type $term Deleted", $lsid ); } =head2 retrieveAllServiceTypes =cut sub retrieveAllServiceTypes { my ($self) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyservice' ); my $types = $adaptor->new_query_service(); my %response; foreach (@$types) { $response{ $_->{'service_type'} } = [$_->{'description'}, $_->{'service_lsid'}, $_->{'contact_email'}, $_->{'authority'}, $_->{'parent_type'}, $_->{'parent_lsid'}]; #UNCOMMENT } return \%response; } =head2 retrieveAllNamespaceTypes =cut sub retrieveAllNamespaceTypes { my ($self) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobynamespace' ); my $types = $adaptor->query_namespace(); my %response; foreach (@$types) { $response{ $_->{namespace_type} } = [$_->{description}, $_->{namespace_lsid}, $_->{authority}, $_->{contact_email}]; } return \%response; } =head2 retrieveAllObjectClasses =cut sub retrieveAllObjectClasses { my ($self) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); my $types = $adaptor->query_object(); my %response; foreach (@$types) { $response{ $_->{object_type} } = [$_->{description}, $_->{object_lsid}]; } return \%response; } *retrieveAllObjectTypes = \&retrieveAllObjectClasses; *retrieveAllObjectTypes = \&retrieveAllObjectClasses; =head2 getObjectCommonName =cut sub getObjectCommonName { my ( $self, $URI ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); return undef unless $URI =~ /urn\:lsid/; my $result = $adaptor->query_object(type => $URI); my $row = shift(@$result); my $name = $row->{object_type}; return $name ? $name : $URI; } =head2 getNamespaceCommonName =cut sub getNamespaceCommonName { my ( $self, $URI ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobynamespace' ); return undef unless $URI =~ /urn\:lsid/; my $result = $adaptor->query_namespace(type => $URI); my $row = shift(@$result); my $name = $row->{namespace_type}; return $name ? $name : $URI; } =head2 getServiceCommonName =cut sub getServiceCommonName { my ( $self, $URI ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyservice' ); return undef unless $URI =~ /urn\:lsid/; my $result = $adaptor->query_service(type => $URI); my $row = shift(@$result); my $name = $row->{service_type}; return $name ? $name : $URI; } =head2 getServiceURI =cut sub getServiceURI { my ( $self, $term ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyservice' ); return $term if $term =~ /urn\:lsid/; my $result = $adaptor->query_service(type => $term); my $row = shift(@$result); my $id = $row->{service_lsid}; return $id; } =head2 getObjectURI =cut sub getObjectURI { my ( $self, $term ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyobject' ); return $term if $term =~ /urn\:lsid/; my $result = $adaptor->query_object(type => $term); my $row = shift(@$result); my $id = $row->{object_lsid}; return $id; } =head2 getNamespaceURI =cut sub getNamespaceURI { my ( $self, $term ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobynamespace' ); return $term if $term =~ /urn\:lsid/; my $result = $adaptor->query_namespace(type => $term); my $row = shift(@$result); my $id = $row->{namespace_lsid}; return $id; } =head2 getRelationshipURI consumes ontology (object/service) consumes relationship term as term or LSID =cut sub getRelationshipURI { my ( $self, $ontology, $term ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyrelationship' ); return $term if $term =~ /urn\:lsid/; my $result = $adaptor->query_relationship(type => $term, ontology => $ontology); my $row = shift(@$result); my $id = $row->{relationship_lsid}; return $id; } =head2 getRelationshipTypes =cut sub getRelationshipTypes { my ( $self, %args ) = @_; $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $adaptor = $CONFIG->getDataAdaptor( datasource => 'mobyrelationship' ); my $ontology = $args{'ontology'}; my $OS = MOBY::OntologyServer->new( ontology => "relationship" ); my $defs = $adaptor->query_relationship(ontology => $ontology); my %result; foreach ( @$defs ) { $result{ $_->{relationship_lsid} } = [ $_->{relationship_type}, $_->{authority}, $_->{description} ]; } return \%result; } =head2 RelationshipsDEPRECATED =cut sub RelationshipsDEPRECATED { # this entire subroutine assumes that there is NOT multiple parenting!! my ( $self, %args ) = @_; my $ontology = $args{ontology} ? $args{ontology} : $self->ontology; my $term = $args{term}; my $relationship = $args{relationship}; my $direction = $args{direction} ? $args{direction} : 'root'; my $expand = $args{expand} ? 1 : 0; return unless ( $ontology && $term && ( ( $ontology eq 'service' ) || ( $ontology eq 'object' ) ) ); # convert $term into an LSID if it isn't already if ( $ontology eq 'service' ) { $term = $self->getServiceURI($term); $relationship ||="isa"; my $OS = MOBY::OntologyServer->new(ontology => 'relationship'); $relationship = $OS->getRelationshipURI("service", $relationship); } elsif ( $ontology eq 'object' ) { $term = $self->getObjectURI($term); $relationship ||="isa"; my $OS = MOBY::OntologyServer->new(ontology => 'relationship'); $relationship = $OS->getRelationshipURI("object", $relationship); } my %results; while ( ( $term ne 'urn:lsid:biomoby.org:objectclass:Object' ) && ( $term ne 'urn:lsid:biomoby.org:servicetype:Service' ) ) { my $defs = $self->_doRelationshipsQuery( $ontology, $term, $relationship, $direction ); return {[]} unless $defs; # somethig has gone terribly wrong! my $lsid; my $rel; my $articleName; foreach ( @{$defs} ) { $lsid = $_->[0]; $rel = $_->[1]; $articleName = $_->[2]; $articleName ||=""; $debug && _LOG("\t\tADDING RELATIONSHIP $_ : $lsid to $rel\n"); push @{ $results{$rel} }, [$lsid, $articleName]; } last unless ($expand); last unless ( $direction eq "root" ); # if we aren't going to root, then be careful or we'll loop infnitely $term = $lsid; # this entire subroutine assumes that there is NOT multiple parenting... } return \%results; #results(relationship} = [[lsid1,articleNmae], [lsid2, articleName], [lsid3, articleName]] } =head2 Relationships =cut sub Relationships { my ($self, %args) = @_; my %results; my $term = $args{term}; my $ontology = $args{ontology} ? $args{ontology} : $self->ontology; my $direction = $args{direction} ? $args{direction} : 'root'; $direction = $direction eq 'root'? 'root' : 'leaves'; # map anything else to 'leaves' my $relationship = $args{relationship}; my $expand = $args{expand} ? 1 : 0; # in order to make this function also usable for 'traverseDAG' # we need a more precise definition what to expand. Note that # the default settings assure the behaviour of the old 'expand' param. # 1. expand along the isa relationship? my $isaExpand = $args{isaExpand} ? $args{isaExpand} : $expand; # 2. expand along the inclusion relationship types (has/hasa), # i.e. get inclusions of inclusions? # (Note: this is set when called by 'traverseDAG') my $incExpand = $args{incExpand} ? $args{incExpand} : 0; # 3. explore inclusion relationships for complete isa hierarchy? # (Note: this was fix behaviour of the old 'expand', # but is not used by traverseDAG) my $mapIncToIsa = $args{mapIncToIsa} ? $args{mapIncToIsa} : $expand; # first of all, get ID of query entity, # internally, we will operate on pure IDs # as long as possible... $CONFIG ||= MOBY::Config->new; # exported by Config.pm my $datasource = "moby$ontology"; # like mobyobject, or mobyservice my $adaptor = $CONFIG->getDataAdaptor( datasource => $datasource ); my $queryId; my $query_method = "query_$ontology"; my $result = $adaptor->$query_method(type => $term); my $row = shift @$result; $queryId = $row->{"${ontology}_id"}; return {} unless $queryId; # get all relationships in the database in one query my $relHash = $adaptor->get_all_relationships(direction=>$direction,ontology=>$ontology); # find out which relationships to return # use keys of %$relHash, because these are lsids: # initialize to return all relationships (becomes effective if eg. 'all' was used) my @relList = keys %$relHash; if ( (not $relationship) or # ISA (and nothing else) is the default if nothing specified ($relationship =~ /isa$/i) ) { @relList = grep { /isa$/i } @relList; } elsif ( $relationship =~ /has(a)?$/i ) { # if either has or hasa was specified, use only that @relList = grep { /$relationship$/i } @relList; } # build the isa hierarchy, it's needed in any case... my ($isaLsid) = grep { /isa$/i } keys %$relHash; # we need the lsid... my $isa_hierarchy = $self->_getIsaHierarchy($relHash->{$isaLsid}, $queryId, $direction, $isaExpand); # prepare the hash for storing HAS/HASA relationship details my $hasRelDetails; # table fields needed to get entity details: my @fields = ("${ontology}_lsid","${ontology}_type"); # nodes to check for has/hasa relationship my @checkNodes = ($queryId); # mapIncToIsa means that has/hasa has to be checked # not only for the query object alone but also for all # isa ancestors/descendants push @checkNodes, @$isa_hierarchy if $mapIncToIsa; # the result hash will consist of one list for each included relationship type... foreach my $rel ( @relList ) { my @entityQueryList = (); # this collects the unique object ids my @entityResultList = (); # this collects ids of objects to add to the result, maybe not unique # the latter one is not essential to have, the only benefit is # a somehow predictable order in the output... # find out which entities we have to include in the result # and how these are related to each other; # Note: all needed information is present in the relationship hash %$relHash! if ( $rel ne $isaLsid ) { # either HAS or HASA foreach my $node ( @checkNodes ) { my $incls = $self->_getInclusions($relHash,$node,[$rel], $incExpand); foreach my $triplet ( @$incls ) { my ($inclId, $inclArtName, $inclAssert) = @$triplet; $hasRelDetails->{$inclId}->{$inclAssert} = $inclArtName; # can be more than one articleName for each included Object push @entityResultList, $inclId; } } # we have the following structure now for the HAS and HASA... # DB<35> x $hasRelDetails # 0 HASH(0x95cd1bc) # 5371 => HASH(0x95f7fd8) # object type # 10795 => 'Tiny' # related to parent by $ rel relationship # 10796 => 'Small' # 10797 => 'Aliphatic' # 10798 => 'Aromatic' # 10799 => 'Non-polar' # 10800 => 'Polar' # 10801 => 'Charged' # 10802 => 'Positive' # 10803 => 'Negative' # 10804 => 'Hydropathy_KD' # 10805 => 'Hydropathy_OHM' # 10806 => 'Consensus' # set up list of unique object ids for the database lookup @entityQueryList = keys %$hasRelDetails; } else { # ISA @entityQueryList = @$isa_hierarchy; # isa hierarchy is guaranteed to be unique... @entityResultList = @$isa_hierarchy; # ... but still both variables have to be set } # now it's time to move away from pure ids, retrieve details from database: my $details = $adaptor->get_details_for_id_list($ontology, \@fields, \@entityQueryList); my $newstructure; # enhance details with information about relationships and build result hash foreach my $entityId (@entityResultList) { # add articleName slot if necessary next if $details->{$entityId}->{'articleName'}; # we've already processed this one if ( exists $hasRelDetails->{$entityId} ) { # the only things that have RelDetails are HASA/HAS EntityIDs foreach my $assert ( keys %{$hasRelDetails->{$entityId}} ) { # THIS DATA STRUCTURE IS WRONG - IT ASSUMES ONE ARTICLE NAME FOR EACH CONTAINED OBJECT TYPE # NEEDS TO BE REVERSED! my $articleName = $hasRelDetails->{$entityId}->{$assert}; my $objectTypeLSID = $details->{entityId}->{object_lsid}; $details->{$entityId}->{'articleName'}->{$articleName} = "Related_by"; # I know, this is a very goofy data structure. What we really # want are keys $details->{entitId}->{articleName} # so taht we can see how often that object is included # by a has or hasa relationship } } elsif ( $ontology eq 'object') { # if it doesn't have a RelDetail, and it is the object ontology we are querying, then its an ISA # for isa, articleName is the empty string $details->{$entityId}->{'articleName'} = ''; } # map ontology specific field names to commons slots: # 1. 'object_lsid'/'service_lsid' -> 'lsid' $details->{$entityId}->{'lsid'} = $details->{$entityId}->{"${ontology}_lsid"} unless exists $details->{$entityId}->{'lsid'}; # do just once foreach object! delete $details->{$entityId}->{"${ontology}_lsid"}; # remove redundant slot # 2. 'object_type'/'service_type' -> 'term' $details->{$entityId}->{'term'} = $details->{$entityId}->{"${ontology}_type"} unless exists $details->{$entityId}->{'term'}; # do just once foreach object! delete $details->{$entityId}->{"${ontology}_type"}; # remove redundant slot # finally, add record to the result hash push @{ $results{$rel} }, $details->{$entityId}; } } return \%results; } sub _getIsaHierarchy { # Finds out the isa hierarchy for the query entity, that is # the parent (the one which it inherits from) if direction is 'root' or # the children (one or more which inherit from it) if direction is 'leaves'. # If 'expand' is set all deeper levels (ancestors or descendants if you like) # are also included. # Note 1: this implementation relies on pure single inheritance! # Note 2: we can use the same method for both directions only because the # provided isaHash is built with the direction in mind, make sure # to have direction consistent! # returned is a reference to a flat list my ($self, $isaHash, $query, $direction, $expand) = @_; my @hierarchy = (); if ( exists $isaHash->{$query} ) { if ( $direction eq 'root' ) { # push the parent entity push @hierarchy, $isaHash->{$query}; # relies on single inheritance! } elsif ( $direction eq 'leaves' ) { # push the direct children push @hierarchy, @{$isaHash->{$query}}; } else { # it has to be either 'root' or 'leaves' warn "_getIsaHierarchy was called with wrong direction indicator, use either 'root' or 'leaves'!\n"; return []; } if ( $expand ) { my @firstLevel = @hierarchy; foreach my $entity ( @firstLevel ) { my $deeperLevels = $self->_getIsaHierarchy($isaHash, $entity, $direction, 1); push @hierarchy, @$deeperLevels; } } return \@hierarchy; } else { # important: anchor the recursion! return []; } } sub _getInclusions { # Finds out the objects related to the query by one of the inclusion # relationships (HAS or HASA). This is the HAS/HASA-analogue to # _getIsaHierarchy, but is more complicated, because the values in # the provided relationship hash ($relHash) are not simple ids but # triplets ("relationship records") in the format of: # [id of relationship partner, articleName, assertion id] # On the other hand, direction does not matter here, because # we have to deal with multi relationships in any case. # Like for ISA, be aware that the relationship hash '$relHash' # is built direction dependant. Make sure to use it consistently! # Note: third argument is a listref of relationship types, that is # it could be called with HAS and HASA (expected are lsids) at # the same time and in this way merge both inclusion relationship # types. However, this usage is not used currently and not tested! # Returned is a reference to a list with each element being # a triplet (listref to a relationship record) as explained above. my ($self, $relHash, $query, $relList, $expand) = @_; my %nodeCheckDone; # for avoiding multiple check of one node (if expand is set) my @allInclusions = (); foreach my $relType ( @$relList ) { # 'root' means: include all relationships where query is the # containing (outer) object; # eg. if A HAS B, and A is query, include this record if ( exists $relHash->{$relType}->{$query} ) { my $relRecords = $relHash->{$relType}->{$query}; foreach my $record ( @$relRecords ) { push @allInclusions, $record; if ( $expand ) { my ($incId, $artName, $assert) = @$record; if ( not exists $nodeCheckDone{$incId} ) { my $deeperInclusions = $self->_getInclusions($relHash, $incId, $relList, 1); push @allInclusions, @$deeperInclusions; $nodeCheckDone{$incId}++; } } } } } return \@allInclusions; # empty if nothing found, this anchors the recursion } =head2 setURI =cut sub setURI { my ( $self, $id ) = @_; my $URI; my ($sec,$min,$hour,$mday,$month,$year, $wday,$yday,$dst) =gmtime(time); my $date = sprintf ("%04d-%02d-%02dT%02d-%02d-%02dZ",$year+1900,$month+1,$mday,$hour,$min,$sec); # $id = lc($id); if ( $self->ontology eq 'object' ) { $URI = "urn:lsid:biomoby.org:objectclass:$id:$date"; } elsif ( $self->ontology eq 'namespace' ) { $URI = "urn:lsid:biomoby.org:namespacetype:$id:$date"; } elsif ( $self->ontology eq 'service' ) { $URI = "urn:lsid:biomoby.org:servicetype:$id:$date"; } elsif ( $self->ontology eq 'relationship' ) { $URI = "urn:lsid:biomoby.org:relationshiptype:$id"; # dont' add version info here } else { $URI = 0; } return $URI; } =head2 traverseDAG =cut sub traverseDAG { my ( $self, $term, $direction ) = @_; my $ontology = $self->ontology; return {} unless $ontology; return {} unless $term; $direction = "root" unless ($direction); return {} unless ( ( $direction eq 'root' ) || ( $direction eq 'leaves' ) ); if ( $ontology eq 'service' ) { $term = $self->getServiceURI($term); } elsif ( $ontology eq 'object' ) { $term = $self->getObjectURI($term); } return {} unless $term; # search term not in db! return {} unless $term =~ /^urn\:lsid/; # now its a URI my $result = {}; # get the types of relationships for the object/service ontology my $relTypeHash = $self->getRelationshipTypes( ontology => $ontology ); my $relHash = $self->Relationships( term => $term, direction => $direction, ontology => $ontology, isaExpand => 1, incExpand => 1, mapIncToIsa => 0, relationship => 'all'); foreach my $relType ( keys %$relTypeHash ) { $result->{$relType} = []; my %tmpHash; # avoid doubles! my $relList = $relHash->{$relType}; foreach my $rel ( @$relList ) { $tmpHash{$rel->{'lsid'}}++; } @{$result->{$relType}} = keys %tmpHash; } return $result; } sub _LOG { return unless $debug; #print join "\n", @_; #print "\n---\n"; #return; open LOG, ">>/tmp/OntologyServer.txt" or die "can't open logfile $!\n"; print LOG join "\n", @_; print LOG "\n---\n"; close LOG; } sub DESTROY { } sub AUTOLOAD { no strict "refs"; my ( $self, $newval ) = @_; $AUTOLOAD =~ /.*::(\w+)/; my $attr = $1; if ( $self->_accessible( $attr, 'write' ) ) { *{$AUTOLOAD} = sub { if ( defined $_[1] ) { $_[0]->{$attr} = $_[1] } return $_[0]->{$attr}; }; ### end of created subroutine ### this is called first time only if ( defined $newval ) { $self->{$attr} = $newval; } return $self->{$attr}; } elsif ( $self->_accessible( $attr, 'read' ) ) { *{$AUTOLOAD} = sub { return $_[0]->{$attr}; }; ### end of created subroutine return $self->{$attr}; } # Must have been a mistake then... croak "No such method: $AUTOLOAD"; } 1;