<commit_before>package Bio::EnsEMBL::RNAseqDB::Bundle;
use strict;
use warnings;
use Carp;
use Moose::Role;

use File::Spec;
use Data::Dumper;
use Readonly;
use File::Path qw(make_path);

use Log::Log4perl qw( :easy );
my $logger = get_logger();

###############################################################################
use Bio::EnsEMBL::RNAseqDB::Common;
my $common = Bio::EnsEMBL::RNAseqDB::Common->new();
Readonly my $PREFIX => $common->get_project_prefix();

# Name of the bundles
Readonly my $GROUP_PREFIX => $PREFIX . 'RNAseq_group_';
Readonly my $TRACK_PREFIX => $PREFIX . 'RNAseq_track_';

# Solr specific constants
Readonly my $SOLR_CHILDREN => '_childDocuments_';
Readonly my $SEARCH_ROOT   => '/vbsearch/details/';
Readonly my $PUBMED_ROOT   => 'https://www.ncbi.nlm.nih.gov/pubmed/';
Readonly my $SRA_URL_ROOT  => 'http://www.ebi.ac.uk/ena/data/view/';

###############################################################################
sub _add_bundle_from_track {
  my ($self, $track_id) = @_;
  
  # Does the bundle already exist?
  my $bundle_req = $self->resultset('Bundle')->search({
      'bundle_tracks.track_id' => $track_id,
  },
  {
    prefetch    => 'bundle_tracks',
  });

  my @res_bundles = $bundle_req->all;
  my $num_bundles = scalar @res_bundles;
  if ($num_bundles > 0) {
    $logger->warn("WARNING: Bundle already exists for track $track_id");
    return;
  }
  
  # Insert a new bundle and a link bundle_tracks
  my $bundle_id = $self->create_bundle_from_track_ids($track_id);
  
  return $bundle_id;
}

sub _add_bundle_track {
  my ($self, $bundle_id, $track_id) = @_;
  
  # First, check that the link doesn't already exists
  $logger->debug("ADDING bundle_track link from $bundle_id to $track_id");
  my $bundle_track_search = $self->resultset('BundleTrack')->search({
      bundle_id => $bundle_id,
      track_id  => $track_id,
  });
  my $links_count = $bundle_track_search->all;
  if ($links_count > 0) {
    $logger->warn("There is already a link between bundle $bundle_id and track $track_id");
    return;
  }
  
  my $bundle_track_insertion = $self->resultset('BundleTrack')->create({
      bundle_id  => $bundle_id,
      track_id   => $track_id,
  });
  $logger->debug("ADDED bundle_track " . $bundle_track_insertion->id);
  return;
}

# INSTANCE METHOD
# Purpose   : insert a bundle in the DB given a list of track_ids
# Parameters: array of track_ids
# Returns   : int bundle_id that was just created
sub create_bundle_from_track_ids {
  my $self = shift;
  my @track_ids = @_;
  
  my $bundle_data = {};
  
  if (@track_ids == 1)  {
    $logger->debug("Bundle: copy data from track " . $track_ids[0]);
    # Retrieve the tracks data to copy to the bundle (auto fields)
    my $tracks = $self->resultset('Track')->search({
        track_id  => $track_ids[0],
      });
    my $track = $tracks->first;
    $bundle_data = {
      title_auto => $track->title_manual || $track->title_auto,
      text_auto  => $track->text_manual  || $track->text_auto,
    };
    $logger->debug("Bundle title = $bundle_data->{title_auto}") if $bundle_data and $bundle_data->{title_auto};
  }
  
  # Add the bundle itself
  my $bundle_insertion = $self->resultset('Bundle')->create($bundle_data);
  
  my $bundle_id = $bundle_insertion->id;
  
  # Add the link from the bundle to the tracks
  for my $track_id (@track_ids) {
    $self->_add_bundle_track($bundle_id, $track_id);
  }
  $logger->debug("ADDED bundle $bundle_id");
  return $bundle_id;
}

sub _get_bundle_tracks_links {
  my ($self, $conditions) = @_;
  $conditions //= {
    'bundle.status' => 'ACTIVE',
  };
  
  my $bundle_track_search = $self->resultset('BundleTrack')->search($conditions,
    {
      prefetch  => 'bundle'
    });
  my @links = $bundle_track_search->all;
  return \@links;
}

sub _inactivate_bundles_for_tracks {
  my ($self, $track_ids_aref) = @_;
  
  # 1) Get the tracks-bundles links
  my @conditions = map { { 'track_id' => $_ } } @$track_ids_aref;
  my $links = $self->_get_bundle_tracks_links(\@conditions);
  
  # 2) Inactivate the corresponding bundles
  my @bundle_ids = map { $_->bundle_id } @$links;
  $self->inactivate_bundles(@bundle_ids);
}

# INSTANCE METHOD
# Purpose   : Inactivate a list of bundles
# Parameters: array of bundle_ids
sub inactivate_bundles {
  my $self = shift;
  my @bundle_ids = @_;
  
  my %bundles_hash = map { $_ => 1 } @bundle_ids;
  @bundle_ids = sort keys %bundles_hash;
  $logger->debug("Inactivate the bundles: " . join(',', @bundle_ids));
  my @bundle_searches = map { { bundle_id => $_ } } @bundle_ids;
  my $tracks_update = $self->resultset('Bundle')->search(
      \@bundle_searches
    )->update({
      status => 'RETIRED',
    });
}

sub get_bundle_id_from_track_id {
  my ($self, $track_id) = @_;
  
  my $links = $self->_get_bundle_tracks_links({
      track_id => $track_id,
      'bundle.status' => 'ACTIVE'
    });
  my @bundle_ids = map { $_->bundle_id } @$links;
  return \@bundle_ids;
}

sub update_bundle {
  my ($self, $bundle_id, $node_content) = @_;
  
  $logger->debug("Update bundle $bundle_id");
  my $bundle_update = $self->resultset('Bundle')->search({
      bundle_id => $bundle_id,
  })->update($node_content);
}

# INSTANCE METHOD
# Purpose   : Merge a list of bundles into one
# Parameters: array of bundle_ids
sub merge_bundles {
  my $self = shift;
  my @bundle_ids = @_;
  
  # Get the track_ids associated with the bundles
  my @track_ids;
  for my $bundle_id (@bundle_ids) {
    push @track_ids, $self->get_bundle_tracks($bundle_id);
  }
  
  # Make a new bundle from the tracks of all the bundles
  $logger->debug("Merge bundles : " . join(",", @bundle_ids));
  my $new_bundle_id = $self->create_bundle_from_track_ids(@track_ids);
  
  # Inactivate the merged bundles, leaving only the merged result
  $self->inactivate_bundles(@bundle_ids);
  
  return $new_bundle_id;
}

# INSTANCE METHOD
# Purpose   : Get the list of tracks contained by a given bundle
# Parameters: one bundle_id
sub get_bundle_tracks {
  my $self = shift;
  my ($bundle_id) = @_;
  
  my $req = $self->resultset('BundleTrack')->search({
      bundle_id => $bundle_id,
  });
  
  my @track_ids = map { $_->track_id } $req->all;
  return @track_ids;
}

# INSTANCE METHOD
# Purpose   : Get the list of all samples metadata for every species
# Parameters: none
# Returns   : Hash ref of samples
sub get_samples {
  my $self = shift;
  
  my %samples;
  my $strains_req = $self->resultset('Strain')->search({
    'assemblies.latest' => 1,
  },
  {
    prefetch  => 'assemblies',
  });
  for my $strain ($strains_req->all) {
    my $name   = $strain->production_name;
    my $sample = $strain->assemblies->next->sample_location;
    $samples{$name} = $sample;
  }
  return \%samples;
}

sub _prepare_hub_activation_link {
  my $self = shift;
  my ($hubs_root, $group) = @_;
  my $samples = $self->get_samples();
  if ($hubs_root and $group) {
    my $hub_url = $hubs_root . '/' . $group->{production_name} . '/' . $group->{trackhub_id} . '/hub.txt';
    my $activation_url = '/' . ucfirst($group->{production_name}) . '/Location/View?'
    . 'r=' . $samples->{$group->{production_name}} . ';'
    . 'contigviewbottom=url:' . $hub_url . ';'
    . 'format=TRACKHUB;'
    . 'menu=' . $group->{label} . ';'
    ;
    return $activation_url;
  }
  return;
}

sub _prepare_track_activation_link {
  my $self = shift;
  my ($hubs_root, $group, $file) = @_;
  my $samples = $self->get_samples();
  if ($hubs_root and $group and $file) {
    my $activation_url = '/' . ucfirst($group->{production_name}) . '/Location/View?'
    . 'r=' . $samples->{$group->{production_name}} . ';'
    . 'contigviewbottom=url:' . $file->{url} . ';'
    ;
    return $activation_url;
  }
  return;
}

sub format_bundles_for_solr {
  my $self = shift;
  my ($opt) = @_;
  
  my $groups  = $opt->{bundles};
  
  my @solr_groups;
  
  # Alter the structure and names to create a valid Solr json for indexing
  for my $group (@$groups) {
    # Select the latest assembly data
    my $as_data = $group->{assemblies};
    my $assembly;
    for my $name (keys %$as_data) {
      if ($as_data->{$name}->{latest}) {
        $assembly = $as_data->{$name};
        $assembly->{name} = $name;
      }
    }
    
    my %solr_group = (
      id                   => $group->{trackhub_id},
      label                => $group->{label},
      description          => $group->{description},
      species              => $group->{species},
      strain_s             => $group->{strain},
      assembly             => $assembly->{name},
      site                 => 'Expression',
      bundle_name          => 'RNA-seq track groups',
      #pubmed_ids_ss        => $group->{publications_pubmeds},
      publications_ss      => $group->{publications},
      publications_ss_urls => $group->{publications_urls},
      hash                 => 'parentDocument',
    );
    
    # Create the activation url
    my $activation_link = $self->_prepare_hub_activation_link($opt->{hubs_url}, $group);
    if ($activation_link) {
      $solr_group{activation_link_s_url} = $activation_link;
      $solr_group{activation_link_s} = 'Load in Genome Browser';
    } else {
      $logger->warn("No hubs_url defined: can't create activation");
    }
    
    foreach my $track (@{ $group->{tracks} }) {
      my $assembly_data = $track->{assemblies}->{ $assembly->{name} };
      my %solr_track = (
        id                            => $track->{id},
        site                          => 'Expression',
        bundle_name                   => 'RNA-seq tracks',
        species                       => $group->{species},
        label                         => $track->{title},
        description                   => $track->{description},<commit_msg>Use default "Browse genome" icon for activation link<commit_after>package Bio::EnsEMBL::RNAseqDB::Bundle;
use strict;
use warnings;
use Carp;
use Moose::Role;

use File::Spec;
use Data::Dumper;
use Readonly;
use File::Path qw(make_path);

use Log::Log4perl qw( :easy );
my $logger = get_logger();

###############################################################################
use Bio::EnsEMBL::RNAseqDB::Common;
my $common = Bio::EnsEMBL::RNAseqDB::Common->new();
Readonly my $PREFIX => $common->get_project_prefix();

# Name of the bundles
Readonly my $GROUP_PREFIX => $PREFIX . 'RNAseq_group_';
Readonly my $TRACK_PREFIX => $PREFIX . 'RNAseq_track_';

# Solr specific constants
Readonly my $SOLR_CHILDREN => '_childDocuments_';
Readonly my $SEARCH_ROOT   => '/vbsearch/details/';
Readonly my $PUBMED_ROOT   => 'https://www.ncbi.nlm.nih.gov/pubmed/';
Readonly my $SRA_URL_ROOT  => 'http://www.ebi.ac.uk/ena/data/view/';

###############################################################################
sub _add_bundle_from_track {
  my ($self, $track_id) = @_;
  
  # Does the bundle already exist?
  my $bundle_req = $self->resultset('Bundle')->search({
      'bundle_tracks.track_id' => $track_id,
  },
  {
    prefetch    => 'bundle_tracks',
  });

  my @res_bundles = $bundle_req->all;
  my $num_bundles = scalar @res_bundles;
  if ($num_bundles > 0) {
    $logger->warn("WARNING: Bundle already exists for track $track_id");
    return;
  }
  
  # Insert a new bundle and a link bundle_tracks
  my $bundle_id = $self->create_bundle_from_track_ids($track_id);
  
  return $bundle_id;
}

sub _add_bundle_track {
  my ($self, $bundle_id, $track_id) = @_;
  
  # First, check that the link doesn't already exists
  $logger->debug("ADDING bundle_track link from $bundle_id to $track_id");
  my $bundle_track_search = $self->resultset('BundleTrack')->search({
      bundle_id => $bundle_id,
      track_id  => $track_id,
  });
  my $links_count = $bundle_track_search->all;
  if ($links_count > 0) {
    $logger->warn("There is already a link between bundle $bundle_id and track $track_id");
    return;
  }
  
  my $bundle_track_insertion = $self->resultset('BundleTrack')->create({
      bundle_id  => $bundle_id,
      track_id   => $track_id,
  });
  $logger->debug("ADDED bundle_track " . $bundle_track_insertion->id);
  return;
}

# INSTANCE METHOD
# Purpose   : insert a bundle in the DB given a list of track_ids
# Parameters: array of track_ids
# Returns   : int bundle_id that was just created
sub create_bundle_from_track_ids {
  my $self = shift;
  my @track_ids = @_;
  
  my $bundle_data = {};
  
  if (@track_ids == 1)  {
    $logger->debug("Bundle: copy data from track " . $track_ids[0]);
    # Retrieve the tracks data to copy to the bundle (auto fields)
    my $tracks = $self->resultset('Track')->search({
        track_id  => $track_ids[0],
      });
    my $track = $tracks->first;
    $bundle_data = {
      title_auto => $track->title_manual || $track->title_auto,
      text_auto  => $track->text_manual  || $track->text_auto,
    };
    $logger->debug("Bundle title = $bundle_data->{title_auto}") if $bundle_data and $bundle_data->{title_auto};
  }
  
  # Add the bundle itself
  my $bundle_insertion = $self->resultset('Bundle')->create($bundle_data);
  
  my $bundle_id = $bundle_insertion->id;
  
  # Add the link from the bundle to the tracks
  for my $track_id (@track_ids) {
    $self->_add_bundle_track($bundle_id, $track_id);
  }
  $logger->debug("ADDED bundle $bundle_id");
  return $bundle_id;
}

sub _get_bundle_tracks_links {
  my ($self, $conditions) = @_;
  $conditions //= {
    'bundle.status' => 'ACTIVE',
  };
  
  my $bundle_track_search = $self->resultset('BundleTrack')->search($conditions,
    {
      prefetch  => 'bundle'
    });
  my @links = $bundle_track_search->all;
  return \@links;
}

sub _inactivate_bundles_for_tracks {
  my ($self, $track_ids_aref) = @_;
  
  # 1) Get the tracks-bundles links
  my @conditions = map { { 'track_id' => $_ } } @$track_ids_aref;
  my $links = $self->_get_bundle_tracks_links(\@conditions);
  
  # 2) Inactivate the corresponding bundles
  my @bundle_ids = map { $_->bundle_id } @$links;
  $self->inactivate_bundles(@bundle_ids);
}

# INSTANCE METHOD
# Purpose   : Inactivate a list of bundles
# Parameters: array of bundle_ids
sub inactivate_bundles {
  my $self = shift;
  my @bundle_ids = @_;
  
  my %bundles_hash = map { $_ => 1 } @bundle_ids;
  @bundle_ids = sort keys %bundles_hash;
  $logger->debug("Inactivate the bundles: " . join(',', @bundle_ids));
  my @bundle_searches = map { { bundle_id => $_ } } @bundle_ids;
  my $tracks_update = $self->resultset('Bundle')->search(
      \@bundle_searches
    )->update({
      status => 'RETIRED',
    });
}

sub get_bundle_id_from_track_id {
  my ($self, $track_id) = @_;
  
  my $links = $self->_get_bundle_tracks_links({
      track_id => $track_id,
      'bundle.status' => 'ACTIVE'
    });
  my @bundle_ids = map { $_->bundle_id } @$links;
  return \@bundle_ids;
}

sub update_bundle {
  my ($self, $bundle_id, $node_content) = @_;
  
  $logger->debug("Update bundle $bundle_id");
  my $bundle_update = $self->resultset('Bundle')->search({
      bundle_id => $bundle_id,
  })->update($node_content);
}

# INSTANCE METHOD
# Purpose   : Merge a list of bundles into one
# Parameters: array of bundle_ids
sub merge_bundles {
  my $self = shift;
  my @bundle_ids = @_;
  
  # Get the track_ids associated with the bundles
  my @track_ids;
  for my $bundle_id (@bundle_ids) {
    push @track_ids, $self->get_bundle_tracks($bundle_id);
  }
  
  # Make a new bundle from the tracks of all the bundles
  $logger->debug("Merge bundles : " . join(",", @bundle_ids));
  my $new_bundle_id = $self->create_bundle_from_track_ids(@track_ids);
  
  # Inactivate the merged bundles, leaving only the merged result
  $self->inactivate_bundles(@bundle_ids);
  
  return $new_bundle_id;
}

# INSTANCE METHOD
# Purpose   : Get the list of tracks contained by a given bundle
# Parameters: one bundle_id
sub get_bundle_tracks {
  my $self = shift;
  my ($bundle_id) = @_;
  
  my $req = $self->resultset('BundleTrack')->search({
      bundle_id => $bundle_id,
  });
  
  my @track_ids = map { $_->track_id } $req->all;
  return @track_ids;
}

# INSTANCE METHOD
# Purpose   : Get the list of all samples metadata for every species
# Parameters: none
# Returns   : Hash ref of samples
sub get_samples {
  my $self = shift;
  
  my %samples;
  my $strains_req = $self->resultset('Strain')->search({
    'assemblies.latest' => 1,
  },
  {
    prefetch  => 'assemblies',
  });
  for my $strain ($strains_req->all) {
    my $name   = $strain->production_name;
    my $sample = $strain->assemblies->next->sample_location;
    $samples{$name} = $sample;
  }
  return \%samples;
}

sub _prepare_hub_activation_link {
  my $self = shift;
  my ($hubs_root, $group) = @_;
  my $samples = $self->get_samples();
  if ($hubs_root and $group) {
    my $hub_url = $hubs_root . '/' . $group->{production_name} . '/' . $group->{trackhub_id} . '/hub.txt';
    my $activation_url = '/' . ucfirst($group->{production_name}) . '/Location/View?'
    . 'r=' . $samples->{$group->{production_name}} . ';'
    . 'contigviewbottom=url:' . $hub_url . ';'
    . 'format=TRACKHUB;'
    . 'menu=' . $group->{label} . ';'
    ;
    return $activation_url;
  }
  return;
}

sub _prepare_track_activation_link {
  my $self = shift;
  my ($hubs_root, $group, $file) = @_;
  my $samples = $self->get_samples();
  if ($hubs_root and $group and $file) {
    my $activation_url = '/' . ucfirst($group->{production_name}) . '/Location/View?'
    . 'r=' . $samples->{$group->{production_name}} . ';'
    . 'contigviewbottom=url:' . $file->{url} . ';'
    ;
    return $activation_url;
  }
  return;
}

sub format_bundles_for_solr {
  my $self = shift;
  my ($opt) = @_;
  
  my $groups  = $opt->{bundles};
  
  my @solr_groups;
  
  # Alter the structure and names to create a valid Solr json for indexing
  for my $group (@$groups) {
    # Select the latest assembly data
    my $as_data = $group->{assemblies};
    my $assembly;
    for my $name (keys %$as_data) {
      if ($as_data->{$name}->{latest}) {
        $assembly = $as_data->{$name};
        $assembly->{name} = $name;
      }
    }
    
    my %solr_group = (
      id                   => $group->{trackhub_id},
      label                => $group->{label},
      description          => $group->{description},
      species              => $group->{species},
      strain_s             => $group->{strain},
      assembly             => $assembly->{name},
      site                 => 'Expression',
      bundle_name          => 'RNA-seq track groups',
      #pubmed_ids_ss        => $group->{publications_pubmeds},
      publications_ss      => $group->{publications},
      publications_ss_urls => $group->{publications_urls},
      hash                 => 'parentDocument',
    );
    
    # Create the activation url
    my $activation_link = $self->_prepare_hub_activation_link($opt->{hubs_url}, $group);
    if ($activation_link) {
      $solr_group{activation_link_s_url} = $activation_link;
      $solr_group{activation_link_s} = '<img src="/sites/default/files/ftp/images/browse_genome.png">';
    } else {
      $logger->warn("No hubs_url defined: can't create activation");
    }
    
    foreach my $track (@{ $group->{tracks} }) {
      my $assembly_data = $track->{assemblies}->{ $assembly->{name} };
      my %solr_track = (
        id                            => $track->{id},
        site                          => 'Expression',
        bundle_name                   => 'RNA-seq tracks',
        species                       => $group->{species},
        label                         => $track->{title},
        description                   => $track->{description},
{'fraction_non_alphanumeric': 0.1676017545649291, 'fraction_numerical': 0.0010200958890135673, 'mean_word_length': 3.146119687037429, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 302, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4919088', 'n_tokens_mistral': 6831, 'n_tokens_neox': 6633, 'n_words': 1492}