Skip to content
Snippets Groups Projects
ConversionSupport.pm 55.3 KiB
Newer Older
}

=head2 get_species_scientific_name

  Arg[1]      : Bio::EnsEMBL::DBSQL::DBAdaptor $dba
  Example     : my $species = $support->get_species_scientific_name($dba);
  Description : Retrieves the species scientific name (Genus species) from the
                meta table
  Return type : String - species scientific name
  Exceptions  : thrown if species name can not be determined from db
  Caller      : general

=cut

sub get_species_scientific_name {
    my ($self, $dba) = @_;
    $dba ||= $self->dba;
    my $sql_tmp = "SELECT meta_value FROM meta WHERE meta_key = \'species.classification\' ORDER BY meta_id";
    my $sql = $dba->dbc->add_limit_clause($sql_tmp,2);
    my $sth = $dba->dbc->db_handle->prepare($sql);
    $sth->execute;
    my @sp;
    while (my @row = $sth->fetchrow_array) {
        push @sp, $row[0];
    }
    $sth->finish;
    my $species = join(" ", reverse @sp);
    $self->throw("Could not determine species scientific name from database.")
        unless $species;
    return $species;
}

=head2 species

  Arg[1]      : (optional) String $species - species name to set
  Example     : my $species = $support->species;
                my $url = "http://vega.sanger.ac.uk/$species/";
  Description : Getter/setter for species name (Genus_species). If not set, it's
                determined from database's meta table
  Return type : String - species name
  Exceptions  : none
  Caller      : general

=cut

sub species {
    my $self = shift;
    $self->{'_species'} = shift if (@_);
    # get species name from database if not set
    unless ($self->{'_species'}) {
        $self->{'_species'} = join('_',
            split(/ /, $self->get_species_scientific_name));
    }
    return $self->{'_species'};
}

  Arg[1]      : (optional) Hashref $chr_hashref - Hashref with chr_name as keys
  Example     : my $chr = { '6-COX' => 1, '1' => 1, 'X' => 1 };
                my @sorted = $support->sort_chromosomes($chr);
  Description : Sorts chromosomes in an intuitive way (numerically, then
                alphabetically). If no chromosome hashref is passed, it's
                retrieve by calling $self->get_chrlength()
  Return type : List - sorted chromosome names
  Exceptions  : thrown if no hashref is provided
  Caller      : general

=cut

sub sort_chromosomes {
    my ($self, $chr_hashref) = @_;
    $chr_hashref = $self->get_chrlength unless ($chr_hashref);
    throw("You have to pass a hashref of your chromosomes")
        unless ($chr_hashref and ref($chr_hashref) eq 'HASH');
    return (sort _by_chr_num keys %$chr_hashref);
}

=head2 _by_chr_num

  Example     : my @sorted = sort _by_chr_num qw(X, 6-COX, 14, 7);
  Description : Subroutine to use in sort for sorting chromosomes. Sorts
                numerically, then alphabetically
  Return type : values to be used by sort
  Exceptions  : none
  Caller      : internal ($self->sort_chromosomes)

=cut

sub _by_chr_num {
    my @awords = split /-/, $a;
    my @bwords = split /-/, $b;

    my $anum = $awords[0];
    my $bnum = $bwords[0];

    if ($anum !~ /^[0-9]*$/) {
        if ($bnum !~ /^[0-9]*$/) {
            return $anum cmp $bnum;
        } else {
            return 1;
        }
    }
    if ($bnum !~ /^[0-9]*$/) {
        return -1;
    }

    if ($anum <=> $bnum) {
        return $anum <=> $bnum;
    } else {
        if ($#awords == 0) {
            return -1;
        } elsif ($#bwords == 0) {
            return 1;
        } else {
            return $awords[1] cmp $bwords[1];
        }
    }
}

=head2 split_chromosomes_by_size

  Arg[1]      : (optional) Int $cutoff - the cutoff in bp between small and
                large chromosomes
  Arg[2]      : (optional) Boolean to include duplicate regions, ie PAR or not
                (default is no)
  Arg[3]      : (optional) Coordsystem version to retrieve
  Example     : my $chr_slices = $support->split_chromosomes_by_size;
                foreach my $block_size (keys %{ $chr_slices }) {
                    print "Chromosomes with blocksize $block_size: ";
                    print join(", ", map { $_->seq_region_name }
                                        @{ $chr_slices->{$block_size} });
                }
  Description : Determines block sizes for storing DensityFeatures on
                chromosomes, and return slices for each chromosome. The block
                size is determined so that you have 150 bins for the smallest
                chromosome over 5 Mb in length. For chromosomes smaller than 5
                Mb, an additional smaller block size is used to yield 150 bins
                for the overall smallest chromosome. This will result in
                reasonable resolution for small chromosomes and high
                performance for big ones.
  Return type : Hashref (key: block size; value: Arrayref of chromosome
                Bio::EnsEMBL::Slices)
  Exceptions  : none
  Caller      : density scripts

=cut

sub split_chromosomes_by_size {
  my $self   = shift;
  my $cutoff = shift || 5000000;
  my $dup    = shift || 0;
  my $cs_version = shift;
  my $slice_adaptor = $self->dba->get_SliceAdaptor;
  my $top_slices;
  if ($self->param('chromosomes')) {
    foreach my $chr ($self->param('chromosomes')) {
      push @{ $top_slices }, $slice_adaptor->fetch_by_region('chromosome', $chr);
    $top_slices = $slice_adaptor->fetch_all('chromosome',$cs_version,0,$dup);
  my ($big_chr, $small_chr, $min_big_chr, $min_small_chr);
  foreach my $slice (@{ $top_slices }) {
    next if ($slice->length eq 10000); #hack for chrY pseudoslice
    if ($slice->length < $cutoff) {
      if (! $min_small_chr or ($min_small_chr > $slice->length)) {
	$min_small_chr = $slice->length;
      }
      # push small chromosomes onto $small_chr
      push @{ $small_chr }, $slice;
    if (! $min_big_chr or ($min_big_chr > $slice->length) && $slice->length > $cutoff) {
      $min_big_chr = $slice->length;
    }
    # push _all_ chromosomes onto $big_chr
    push @{ $big_chr }, $slice;
  }
  my $chr_slices;
  $chr_slices->{int($min_big_chr/150)} = $big_chr if $min_big_chr;
  $chr_slices->{int($min_small_chr/150)} = $small_chr if $min_small_chr;
  return $chr_slices;
=head2 log

  Arg[1]      : String $txt - the text to log
  Arg[2]      : Int $indent - indentation level for log message
  Example     : my $log = $support->log_filehandle;
                $support->log('Log foo.\n', 1);
  Description : Logs a message to the filehandle initialised by calling
                $self->log_filehandle(). You can supply an indentation level
                to get nice hierarchical log messages.
  Return type : true on success
  Exceptions  : thrown when no filehandle can be obtained
  Caller      : general

=cut

sub log {
    my ($self, $txt, $indent) = @_;
    $indent ||= 0;
    
    # strip off leading linebreaks so that indenting doesn't break
    $txt =~ s/^(\n*)//;
    
    $txt = $1."    "x$indent . $txt;
    my $fh = $self->{'_log_filehandle'};
    throw("Unable to obtain log filehandle") unless $fh;
    print $fh "$txt";
    return(1);
}

=head2 log_warning

  Arg[1]      : String $txt - the warning text to log
  Arg[2]      : Int $indent - indentation level for log message
Steve Trevanion's avatar
Steve Trevanion committed
  Arg[3]      : Bool - add a line break before warning if true
  Example     : my $log = $support->log_filehandle;
                $support->log_warning('Log foo.\n', 1);
  Description : Logs a message via $self->log and increases the warning counter.
  Return type : true on success
  Exceptions  : none
  Caller      : general

=cut

sub log_warning {
Web Admin's avatar
Web Admin committed
    my ($self, $txt, $indent, $break) = @_;
    $txt = "WARNING: " . $txt;
	$txt = "\n$txt" if ($break);
    $self->log($txt, $indent);
    $self->{'_warnings'}++;
    return(1);
}

=head2 log_error

  Arg[1]      : String $txt - the error text to log
  Arg[2]      : Int $indent - indentation level for log message
  Example     : my $log = $support->log_filehandle;
                $support->log_error('Log foo.\n', 1);
  Description : Logs a message via $self->log and exits the script.
  Return type : none
  Exceptions  : none
  Caller      : general

=cut

sub log_error {
    my ($self, $txt, $indent) = @_;
    $txt = "ERROR: ".$txt;
    $self->log($txt, $indent);
    $self->log("Exiting.\n");
    exit;
}

=head2 log_verbose

  Arg[1]      : String $txt - the warning text to log
  Arg[2]      : Int $indent - indentation level for log message
  Example     : my $log = $support->log_filehandle;
                $support->log_verbose('Log this verbose message.\n', 1);
  Description : Logs a message via $self->log if --verbose option was used
  Return type : TRUE on success, FALSE if not verbose
  Exceptions  : none
  Caller      : general

=cut

sub log_verbose {
    my ($self, $txt, $indent) = @_;

    return(0) unless $self->param('verbose');

    $self->log($txt, $indent);
    return(1);
}

=head2 log_stamped

  Arg[1]      : String $txt - the warning text to log
  Arg[2]      : Int $indent - indentation level for log message
  Example     : my $log = $support->log_filehandle;
                $support->log_stamped('Log this stamped message.\n', 1);
  Description : Appends timestamp and memory usage to a message and logs it via
                $self->log
  Return type : TRUE on success
  Exceptions  : none
  Caller      : general

=cut

sub log_stamped {
    my ($self, $txt, $indent) = @_;
    
    # append timestamp and memory usage to log text
    $txt =~ s/(\n*)$//;
    $txt .= " ".$self->date_and_mem.$1;
    
    $self->log($txt, $indent);
    return(1);
}

  Arg[1]      : (optional) String $mode - file access mode
  Example     : my $log = $support->log_filehandle;
                # print to the filehandle
                print $log 'Lets start logging...\n';
                # log via the wrapper $self->log()
                $support->log('Another log message.\n');
  Description : Returns a filehandle for logging (STDERR by default, logfile if
                set from config or commandline). You can use the filehandle
                directly to print to, or use the smart wrapper $self->log().
                Logging mode (truncate or append) can be set by passing the
                mode as an argument to log_filehandle(), or with the
                --logappend commandline option (default: truncate)
  Return type : Filehandle - the filehandle to log to
  Exceptions  : thrown if logfile can't be opened
  Caller      : general

=cut

sub log_filehandle {
    my ($self, $mode) = @_;
    $mode ||= '>';
    $mode = '>>' if ($self->param('logappend'));
    my $fh = \*STDERR;
    if (my $logfile = $self->param('logfile')) {
        if (my $logpath = $self->param('logpath')) {
            unless (-e $logpath) {
                system("mkdir $logpath") == 0 or
                    $self->log_error("Can't create log dir $logpath: $!\n");
            }
        open($fh, "$mode", $logfile) or throw(
            "Unable to open $logfile for writing: $!");
    }
    $self->{'_log_filehandle'} = $fh;
    return $self->{'_log_filehandle'};
}

=head2 filehandle

  Arg[1]      : String $mode - file access mode
  Arg[2]      : String $file - input or output file
  Example     : my $fh = $support->filehandle('>>', '/path/to/file');
                # print to the filehandle
                print $fh 'Your text goes here...\n';
  Description : Returns a filehandle (*STDOUT for writing, *STDIN for reading
                by default) to print to or read from.
  Return type : Filehandle - the filehandle
  Exceptions  : thrown if file can't be opened
  Caller      : general

=cut

sub filehandle {
    my ($self, $mode, $file) = @_;
    $mode ||= ">";
    my $fh;
    if ($file) {
        open($fh, "$mode", $file) or throw(
            "Unable to open $file for writing: $!");
    } elsif ($mode =~ />/) {
        $fh = \*STDOUT;
    } elsif ($mode =~ /</) {
        $fh = \*STDIN;
    }
    return $fh;
}

  Example     : $support->init_log;
  Description : Opens a filehandle to the logfile and prints some header
                information to this file. This includes script name, date, user
                running the script and parameters the script will be running
                with.
  Return type : Filehandle - the log filehandle
  Exceptions  : none
  Caller      : general

=cut

sub init_log {
    my $self = shift;

    # get a log filehandle
    my $log = $self->log_filehandle;

    # print script name, date, user who is running it
    my $hostname = `hostname`;
    chomp $hostname;
    my $script = "$hostname:$Bin/$Script";
    my $user = `whoami`;
    chomp $user;
    $self->log("Script: $script\nDate: ".$self->date."\nUser: $user\n");

    # print parameters the script is running with
    $self->log("Parameters:\n\n");
    $self->log($self->list_all_params);
    # remember start time
    $self->{'_start_time'} = time;

    return $log;
  Example     : $support->finish_log;
  Description : Writes footer information to a logfile. This includes the
                number of logged warnings, timestamp and memory footprint.
  Return type : TRUE on success
  Exceptions  : none
  Caller      : general

=cut

sub finish_log {
    my $self = shift;
Patrick Meidl's avatar
Patrick Meidl committed
    $self->log("\nAll done. ".$self->warnings." warnings. ");
    if ($self->{'_start_time'}) {
        $self->log("Runtime ");
        my $diff = time - $self->{'_start_time'};
        my $sec = $diff % 60;
        $diff = ($diff - $sec) / 60;
        my $min = $diff % 60;
        my $hours = ($diff - $min) / 60;
        $self->log("${hours}h ${min}min ${sec}sec ");
    }
    $self->log($self->date_and_mem."\n\n");
    return(1);
}

=head2 date_and_mem

  Example     : print LOG "Time, memory usage: ".$support->date_and_mem."\n";
  Description : Prints a timestamp and the memory usage of your script.
  Return type : String - timestamp and memory usage
  Exceptions  : none
  Caller      : general

=cut

sub date_and_mem {
    my $date = strftime "%Y-%m-%d %T", localtime;
    my $mem = `ps -p $$ -o vsz |tail -1`;
    chomp $mem;
    return "[$date, mem $mem]";
}

=head2 date

  Example     : print "Date: " . $support->date . "\n";
  Description : Prints a nicely formatted timestamp (YYYY-DD-MM hh:mm:ss)
  Return type : String - the timestamp
  Exceptions  : none
  Caller      : general

=cut

sub date {
    return strftime "%Y-%m-%d %T", localtime;
}

Steve Trevanion's avatar
Steve Trevanion committed
=head2 format_time

  Example     : print $support->format_time($gene->modifed_date) . "\n";
  Description : Prints timestamps from the database
  Return type : String - nicely formatted time stamp
  Exceptions  : none
  Caller      : general

=cut


sub date_format { 
  my( $self, $time, $format ) = @_;
  my( $d,$m,$y) = (localtime($time))[3,4,5];
  my %S = ('d'=>sprintf('%02d',$d),'m'=>sprintf('%02d',$m+1),'y'=>$y+1900);
  (my $res = $format ) =~s/%(\w)/$S{$1}/ge;
  return $res;
}


=head2 mem

  Example     : print "Memory usage: " . $support->mem . "\n";
  Description : Prints the memory used by your script. Not sure about platform
                dependence of this call ...
  Return type : String - memory usage
  Exceptions  : none
  Caller      : general

=cut

sub mem {
    my $mem = `ps -p $$ -o vsz |tail -1`;
Patrick Meidl's avatar
Patrick Meidl committed
=head2 commify

  Arg[1]      : Int $num - a number to commify
  Example     : print "An easy to read number: ".$self->commify(100000000);
                # will print 100,000,000
  Description : put commas into a number to make it easier to read
  Return type : a string representing the commified number
  Exceptions  : none
  Caller      : general
  Status      : stable

=cut

sub commify {
  my $self = shift;
  my $num = shift;

  $num = reverse($num);
  $num =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;

  return scalar reverse $num;
}

Web Admin's avatar
Web Admin committed
=head2 fetch_non_hidden_slices

  Arg[1]      : B::E::SliceAdaptor
  Arg[2]      : B::E::AttributeAdaptor
  Arg[3]      : string $coord_system_name (optional) - 'chromosome' by default
  Arg[4]      : string $coord_system_version (optional) - 'otter' by default
Steve Trevanion's avatar
Steve Trevanion committed
  Example     : $chroms = $support->fetch_non_hidden_slice($sa,$aa);
  Description : retrieve all slices from a loutre database that don't have a hidden attribute
Web Admin's avatar
Web Admin committed
  Return type : arrayref
  Caller      : general
  Status      : stable

=cut

sub fetch_non_hidden_slices {
Steve Trevanion's avatar
Steve Trevanion committed
  my $self = shift;
  my $aa   = shift or throw("You must supply an attribute adaptor");
  my $sa   = shift or throw("You must supply a slice adaptor");
  my $cs   = shift || 'chromosome';
  my $cv   = shift || 'Otter';
  my $visible_chroms;
  foreach my $chrom ( @{$sa->fetch_all($cs,$cv)} ) {
    my $chrom_name = $chrom->name;
    my $attribs = $aa->fetch_all_by_Slice($chrom,'hidden');
    if ( scalar(@$attribs) > 1 ) {
      $self->log_warning("More than one hidden attribute for chromosome $chrom_name\n");
    }
    elsif ($attribs->[0]->value == 0) {				
      push @$visible_chroms, $chrom;
    }
    elsif ($attribs->[0]->value == 1) {	
      $self->log_verbose("chromosome $chrom_name is hidden\n");	
    }
    else {
      $self->log_warning("No hidden attribute for chromosome $chrom_name\n");
    }
  }
  return $visible_chroms;
Steve Trevanion's avatar
Steve Trevanion committed
=head2 get_non_hidden_slice_names

  Arg[1]      : B::E::SliceAdaptor
  Arg[2]      : B::E::AttributeAdaptor
  Arg[3]      : string $coord_system_name (optional) - 'chromosome' by default
  Arg[4]      : string $coord_system_version (optional) - 'otter' by default
  Example     : $chrom_names = $support->get_non_hidden_slice_names($sa,$aa);
  Description : retrieve names of all slices from a loutre database that don't have a hidden attribute
  Return type : arrayref of names of all non-hidden slices
  Caller      : general
  Status      : stable

=cut

sub get_non_hidden_slice_names {
  my $self = shift;
  my $aa   = shift or throw("You must supply an attribute adaptor");
  my $sa   = shift or throw("You must supply a slice adaptor");
  my $cs   = shift || 'chromosome';
  my $cv   = shift || 'Otter';
  my $visible_chrom_names;
  foreach my $chrom ( @{$sa->fetch_all($cs,$cv)} ) {
    my $chrom_name = $chrom->seq_region_name;
    my $attribs = $aa->fetch_all_by_Slice($chrom,'hidden');
    if ( scalar(@$attribs) > 1 ) {
      $self->log_warning("More than one hidden attribute for chromosome $chrom_name\n");
    }
    elsif ($attribs->[0]->value == 0) {				
      push @$visible_chrom_names, $chrom_name;
    }
    elsif ($attribs->[0]->value == 1) {	
      $self->log_verbose("chromosome $chrom_name is hidden\n");	
    }
    else {
      $self->log_warning("No hidden attribute for chromosome $chrom_name\n");
    }
  }
  return $visible_chrom_names;
}


Steve Trevanion's avatar
Steve Trevanion committed
=head2 get_wanted_chromosomes

  Arg[1]      : B::E::U::ConversionSupport
  Arg[2]      : B::E::SliceAdaptor
  Arg[3]      : B::E::AttributeAdaptor
  Arg[4]      : string $coord_system_name (optional) - 'chromosome' by default
  Arg[5]      : string $coord_system_version (optional) - 'otter' by default
Steve Trevanion's avatar
Steve Trevanion committed
  Example     : $chr_names = $support->get_wanted_chromosomes($laa,$lsa);
Steve Trevanion's avatar
Steve Trevanion committed
  Description : retrieve names of slices from a lutra database that are ready for dumping to Vega.
                Deals with list of names to ignore (ignore_chr = LIST)
Steve Trevanion's avatar
Steve Trevanion committed
  Return type : arrayref of slices
Steve Trevanion's avatar
Steve Trevanion committed
  Caller      : general
  Status      : stable

=cut

sub get_wanted_chromosomes {
Steve Trevanion's avatar
Steve Trevanion committed
  my $self = shift;
  my $aa   = shift or throw("You must supply an attribute adaptor");
  my $sa   = shift or throw("You must supply a slice adaptor");
  my $cs   = shift || 'chromosome';
  my $cv   = shift || 'Otter';
  my $export_mode = $self->param('release_type');
  my $release = $self->param('vega_release');
  my $names;
  my $chroms  = $self->fetch_non_hidden_slices($aa,$sa,$cs,$cv);
Steve Trevanion's avatar
Steve Trevanion committed
 CHROM:
Steve Trevanion's avatar
Steve Trevanion committed
  foreach my $chrom (@$chroms) {
    my $attribs = $aa->fetch_all_by_Slice($chrom);
    my $vals = $self->get_attrib_values($attribs,'vega_export_mod');
    if (scalar(@$vals > 1)) {
      $self->log_warning ("Multiple attribs for \'vega_export_mod\', please fix before continuing");
      exit;
    }
    next CHROM if (! grep { $_ eq $export_mode} @$vals);
    $vals =  $self->get_attrib_values($attribs,'vega_release',$release);	
    if (scalar(@$vals > 1)) {
      $self->log_warning ("Multiple attribs for \'vega_release\' value = $release , please fix before continuing");
      exit;
    }
    next CHROM if (! grep { $_ eq $release} @$vals);
    my $name = $chrom->seq_region_name;
    if (my @ignored = $self->param('ignore_chr')) {
      next CHROM if (grep {$_ eq $name} @ignored);
    }
    push @{$names}, $name;
  }
  return $names;
Web Admin's avatar
Web Admin committed

=head2 get_attrib_values

  Arg[1]      : Arrayref of B::E::Attributes
  Arg[2]      : 'code' to search for
  Arg[3]      : 'value' to search for (optional)
  Example     : my $c = $self->get_attrib_values($attribs,'name'));
Steve Trevanion's avatar
Steve Trevanion committed
  Description : (i) In the absence of an attribute value argument, examines an arrayref
Web Admin's avatar
Web Admin committed
                of B::E::Attributes for a particular attribute type, returning the values
Steve Trevanion's avatar
Steve Trevanion committed
                for each attribute of that type. Can therefore be used to test for the
                number of attributes of that type.
                (ii) In the presence of the optional value argument it returns all
                attributes with that value ie can be used to test for the presence of an
                attribute with that particular value.
Web Admin's avatar
Web Admin committed
  Return type : arrayref of values for that attribute
  Caller      : general
  Status      : stable

=cut

sub get_attrib_values {
Steve Trevanion's avatar
Steve Trevanion committed
  my $self    = shift;
  my $attribs = shift;
  my $code    = shift;
  my $value   = shift;
  if (my @atts = grep {$_->code eq $code } @$attribs) {
    my $r = [];
    if ($value) {
      if (my @values = grep {$_->value eq $value} @atts) {
	foreach (@values) {
	  push @$r, $_->value;
Web Admin's avatar
Web Admin committed
	}
Steve Trevanion's avatar
Steve Trevanion committed
	return $r;
      }
      else {
	return [];
      }
    }
    else {
      foreach (@atts) {
	push @$r, $_->value;
      }
      return $r;
    }
  }
  else {
    return [];
  }
Web Admin's avatar
Web Admin committed
}

=head2 fix_attrib_value

Steve Trevanion's avatar
Steve Trevanion committed
  Arg[1]      : Arrayref of existing B::E::Attributes
Web Admin's avatar
Web Admin committed
  Arg[2]      : dbID of object
  Arg[3]      : name of object (just for reporting)
  Arg[4]      : attrib_type.code
  Arg[5]      : attrib_type.value
Steve Trevanion's avatar
Steve Trevanion committed
  Arg[6]      : interactive ? (0 by default)
  Arg[7]      : table
Web Admin's avatar
Web Admin committed
  Example     : $support->fix_attrib_value($attribs,$chr_id,$chr_name,'vega_export_mod','N',1);
  Description : adds a new attribute to an object, or updates an existing attribute with a new value
                Can be run in interactive or non-interactive mode (default)
Steve Trevanion's avatar
Steve Trevanion committed
  Return type : arrayref of results
Web Admin's avatar
Web Admin committed
  Caller      : general
  Status      : only ever tested with seq_region_attributes to date

=cut

sub fix_attrib_value {
	my $self        = shift;
	my $attribs     = shift;
	my $id          = shift;
	my $name        = shift;
	my $code        = shift;
	my $value       = shift;
	my $interact    = shift || 0;
	my $table       = shift || 'seq_region_attrib';

Steve Trevanion's avatar
Steve Trevanion committed
	#transiently set interactive parameter to zero
Web Admin's avatar
Web Admin committed
	my $int_before;
	if (! $interact) {
		$int_before = $self->param('interactive');
		$self->param('interactive',0);
	}

	#get any existing value(s) for this attribute
	my $existings = $self->get_attrib_values($attribs,$code);
	
	#add a new attribute if there is none...
	if (! @$existings ) {
		if ($self->user_proceed("Do you want to set $name attrib (code = $code) to value $value ?")) {
			my $r = $self->store_new_attribute($id,$code,$value);
			
			#reset interactive parameter
			$self->param('interactive',$int_before) if (! $interact);
			return $r;
		}
	}
	#...warn and exit if you're trying to update more than one value for the same attribute...
	elsif (scalar @$existings > 1) {
		$self->log_warning("You shouldn't be trying to update multiple attributes with the same code at once ($name:$code,$value), looks like you have duplicate entries in the (seq_region_)attrib table\n");
		exit;
	}

Steve Trevanion's avatar
Steve Trevanion committed
	#...or update an attribute with new values...
Web Admin's avatar
Web Admin committed
	else {
		my $existing = $existings->[0];
		if ($existing ne $value) {
			if ($self->user_proceed("Do you want to reset $name attrib (code = $code) from $existing to $value ?")) {
				my $r = $self->update_attribute($id,$code,$value);
				$self->param('interactive',$int_before) if (! $interact);
				push @$r, $existing;
				return $r;
			}
		}
		#...or make no change
		else {
			$self->param('interactive',$int_before) if (! $interact);
			return [];
		}
	}
}

=head2 _get_attrib_id

  Arg[1]      : attrib_type.code 
  Arg[2]      : database handle 
  Example     : $self->_get_attrib_id('name',$dbh)
  Description : get attrib_type.attrib_type_id from a attrib_type.code
  Return type : attrib_type.attrib_type_id 
  Caller      : internal
  Status      : stable

=cut

sub _get_attrib_id {
	my $self        = shift;
	my $attrib_code = shift;
	my $dbh         = shift;
	my ($attrib_id) = $dbh->selectrow_array(
		qq(select attrib_type_id
           from attrib_type
           where code = ?),
			{},
		($attrib_code)
	);
	if (! $attrib_id) {
		$self->log_warning("There is no attrib_type_id for code $attrib_code, please patch the attrib_table\n");
		exit;
	}
	else {
		return $attrib_id;
	}
}

=head2 store_new_attribute

  Arg[1]      : seq_region.seq_region_id
  Arg[2]      : attrib_type.code
  Arg[3]      : attrib_type.value
  ARG[4]      : table to update (seq_region_attribute by default)
  Example     : $support->store_new_attribute(23,name,5);
  Description : uses MySQL to store an entry (code and value) in an attribute table 
                (seq_region_attrib by default)
  Return type : array_ref
  Caller      : general
  Status      : stable

=cut

sub store_new_attribute {
	my $self         = shift;
	my $sr_id        = shift;
	my $attrib_code  = shift;
Steve Trevanion's avatar
Steve Trevanion committed
	my $attrib_value = shift || '';
Web Admin's avatar
Web Admin committed
	my $table        = shift || 'seq_region_attrib';

	#get database handle
	my $dbh = $self->get_dbconnection('loutre');
	#get attrib_type_id for this particular attribute
	my $attrib_id = $self->_get_attrib_id($attrib_code,$dbh);
	#store
	my $r = $dbh->do(
		qq(insert into $table
           values (?,?,?)),
        {},
		($sr_id,$attrib_id,$attrib_value)
	);
	return ['Stored',$r];
}

=head2 update_attribute

  Arg[1]      : seq_region.seq_region_id
  Arg[2]      : attrib_type.code
  Arg[3]      : attrib_type.value
  ARG[4]      : table to update (seq_region_attribute by default)
  Example     : $support->update_attribute(23,name,5);
  Description : uses MySQL to update an attribute table (seq_region_attrib by default)
  Return type : array_ref
  Caller      : general
  Status      : stable

=cut

sub update_attribute {
	my $self = shift;
	my $sr_id = shift;
	my $attrib_code  = shift;
	my $attrib_value = shift;
	my $table        = shift || 'seq_region_attrib';
	my $dbh = $self->get_dbconnection('loutre');
	my $attrib_id = $self->_get_attrib_id($attrib_code,$dbh);
	#update
	my $r = $dbh->do(
		qq(update $table
           set value = ?
           where seq_region_id = $sr_id
           and attrib_type_id = $attrib_id),
        {},
		($attrib_value)
	);
	return ['Updated',$r];
}