<commit_before>## no critic (NamingConventions::Capitalization, Subroutines::RequireFinalReturn)
package CSG::Mapper::Command::update;

use CSG::Mapper -command;
use CSG::Base;
use CSG::Constants qw(:basic :mapping);
use CSG::Mapper::DB;
use CSG::Mapper::Exceptions;
use CSG::Mapper::Logger;

my $schema = CSG::Mapper::DB->new();

sub opt_spec {
  return (
    ['meta-id=i',        'Job meta data db record id', {required => 1}],
    ['start',            'Mark a sample started'],
    ['job-id=i',         'Add the clusters job id for a given sample'],
    ['node=s',           'Update what node(s) a sample is running on in the cluster'],
    ['state=s',          'Update the jobs state (valid states: failed|submitted|completed|cancelled|requested)'],
    ['exit-code=i',      'Update the exit code from a given sample'],
    ['step=s',           'Job step (e.g. bam2fastq, local-align, cloud-align)'],
    ['fastq-list=s',     'List of fastq files generated by bamUtil'],
    ['fastq-complete=s', 'Completed alignment of fastq'],
  );
}

sub validate_args {
  my ($self, $opts, $args) = @_;

  my $meta = $schema->resultset('Job')->find($opts->{meta_id});
  unless ($meta) {
    $self->usage_error('unable to locate the job meta data record');
  }

  if ($opts->{state}) {
    my $state = $schema->resultset('State')->find({name => $opts->{state}});
    unless ($state) {
      $self->usage_error('invalid job state');
    }

    $self->{stash}->{state} = $state;
  }

  if ($opts->{start} and $meta->started_at) {
    $self->usage_error('job has already started');
  }

  if (defined $opts->{exit_code} and $meta->ended_at) {
    $self->usage_error('job has already ended');
  }

  if ($opts->{step}) {
    my $step = $schema->resultset('Step')->find({name => $opts->{step}});
    unless ($step) {
      $self->usage_error('invalid job step');
    }

    $self->{stash}->{step} = $step;
  }

  if ($opts->{fastq_list}) {
    unless (-e $opts->{fastq_list}) {
      $self->usage_error('fastq list does not exist');
    }
  }

  $self->{stash}->{meta} = $meta;
}

sub execute {
  my ($self, $opts, $args) = @_;

  my $meta   = $self->{stash}->{meta};
  my $state  = $self->{stash}->{state};
  my $step   = $self->{stash}->{step};
  my $logger = CSG::Mapper::Logger->new(job_id => $meta->id);
  my $params = {};

  if ($opts->{start}) {
    $params->{started_at} = $schema->now();
    $state = $schema->resultset('State')->find({name => 'started'});
  }

  if ($opts->{job_id}) {
    $params->{job_id} = $opts->{job_id};
  }

  if ($opts->{node}) {
    $params->{node} = $opts->{node};
  }

  if (defined $opts->{exit_code}) {
    $params->{exit_code} = $opts->{exit_code};
    $params->{ended_at}  = $schema->now();
  }

  if (keys %{$params}) {
    for my $key (keys %{$params}) {
      $logger->info("updating $key to $params->{$key}");
    }

    $meta->update($params);
  }

  if ($state) {
    my $log = sprintf "changing result state from '%s' to '%s' for step '%s'", $meta->result->current_state, $state->name, $step->name;
    $logger->info($log);

    $meta->result->add_to_results_states_steps(
      {
        state_id => $state->id,
        step_id  => $step->id,
        job_id   => $meta->id,
      }
    );
  }

  if ($opts->{fastq_list}) {
    my @lines = io($opts->{fastq_list})->chomp->getlines;
    shift @lines;

    if (scalar @lines == 0) {
      CSG::Mapper::Exceptions::Sample::Fastq::ListEmpty->throw();
    }

    for my $line (@lines) {
      my ($sample_id, $fastq1, $fastq2, @rg) = split(/$TAB/, $line);

      next if $meta->result->sample->has_fastqs($fastq1);

      unless ($sample_id eq $meta->result->sample->sample_id) {
        CSG::Mapper::Exceptions::Sample::FastqMismatch->throw();
      }

      unless (-e $fastq1) {
        CSG::Mapper::Exceptions::Sample::FastqNotFound->throw();
      }

      unless ($rg[0] eq '@RG') {
        CSG::Mapper::Exceptions::Sample::Fastq::MissingRG->throw();
      }

      # XXX - It appears read group headers are not the same across
      #       samples making this test blow up when it shouldn't. might
      #       bring it back if I can pin down these headers more.
      #
      # my $headers = {map {(split(/$COLON/, $_))[0] => 1} @rg[1 .. $#rg]};
      # for (@READ_GROUP_FIELDS) {
      #   unless (exists $headers->{$_}) {
      #     CSG::Mapper::Exceptions::Sample::Fastq::MissingHeader->throw(header => $_);
      #   }
      # }

      $meta->result->sample->add_to_fastqs(
        {
          build      => $meta->result->build,
          path       => $fastq1,
          read_group => join('\t', @rg),
        }
      );

      $logger->info('added fastq ' . $fastq1 . ' to sample ' . $meta->result->sample->sample_id);
    }
  }

  if ($opts->{fastq_complete}) {
    my $fastq = $meta->result->sample->fastqs->search({path => $opts->{fastq_complete}});

    if ($fastq->count > 1) {
      croak 'Found multiple fastq files when there should only be one';
    }

    unless (-e $fastq->path) {
      croak 'unable to locate fastq on disk';
    }

    $logger->info('removing fastq from disk');
    unlink($fastq->path);

    $logger->info('removing record from database');
    $fastq->delete;
  }
}

1;

__END__

=head1

CSG::Mapper::Command::update - update remapping jobs<commit_msg>we have a resultset object so just use the first result in the list<commit_after>## no critic (NamingConventions::Capitalization, Subroutines::RequireFinalReturn)
package CSG::Mapper::Command::update;

use CSG::Mapper -command;
use CSG::Base;
use CSG::Constants qw(:basic :mapping);
use CSG::Mapper::DB;
use CSG::Mapper::Exceptions;
use CSG::Mapper::Logger;

my $schema = CSG::Mapper::DB->new();

sub opt_spec {
  return (
    ['meta-id=i',        'Job meta data db record id', {required => 1}],
    ['start',            'Mark a sample started'],
    ['job-id=i',         'Add the clusters job id for a given sample'],
    ['node=s',           'Update what node(s) a sample is running on in the cluster'],
    ['state=s',          'Update the jobs state (valid states: failed|submitted|completed|cancelled|requested)'],
    ['exit-code=i',      'Update the exit code from a given sample'],
    ['step=s',           'Job step (e.g. bam2fastq, local-align, cloud-align)'],
    ['fastq-list=s',     'List of fastq files generated by bamUtil'],
    ['fastq-complete=s', 'Completed alignment of fastq'],
  );
}

sub validate_args {
  my ($self, $opts, $args) = @_;

  my $meta = $schema->resultset('Job')->find($opts->{meta_id});
  unless ($meta) {
    $self->usage_error('unable to locate the job meta data record');
  }

  if ($opts->{state}) {
    my $state = $schema->resultset('State')->find({name => $opts->{state}});
    unless ($state) {
      $self->usage_error('invalid job state');
    }

    $self->{stash}->{state} = $state;
  }

  if ($opts->{start} and $meta->started_at) {
    $self->usage_error('job has already started');
  }

  if (defined $opts->{exit_code} and $meta->ended_at) {
    $self->usage_error('job has already ended');
  }

  if ($opts->{step}) {
    my $step = $schema->resultset('Step')->find({name => $opts->{step}});
    unless ($step) {
      $self->usage_error('invalid job step');
    }

    $self->{stash}->{step} = $step;
  }

  if ($opts->{fastq_list}) {
    unless (-e $opts->{fastq_list}) {
      $self->usage_error('fastq list does not exist');
    }
  }

  $self->{stash}->{meta} = $meta;
}

sub execute {
  my ($self, $opts, $args) = @_;

  my $meta   = $self->{stash}->{meta};
  my $state  = $self->{stash}->{state};
  my $step   = $self->{stash}->{step};
  my $logger = CSG::Mapper::Logger->new(job_id => $meta->id);
  my $params = {};

  if ($opts->{start}) {
    $params->{started_at} = $schema->now();
    $state = $schema->resultset('State')->find({name => 'started'});
  }

  if ($opts->{job_id}) {
    $params->{job_id} = $opts->{job_id};
  }

  if ($opts->{node}) {
    $params->{node} = $opts->{node};
  }

  if (defined $opts->{exit_code}) {
    $params->{exit_code} = $opts->{exit_code};
    $params->{ended_at}  = $schema->now();
  }

  if (keys %{$params}) {
    for my $key (keys %{$params}) {
      $logger->info("updating $key to $params->{$key}");
    }

    $meta->update($params);
  }

  if ($state) {
    my $log = sprintf "changing result state from '%s' to '%s' for step '%s'", $meta->result->current_state, $state->name, $step->name;
    $logger->info($log);

    $meta->result->add_to_results_states_steps(
      {
        state_id => $state->id,
        step_id  => $step->id,
        job_id   => $meta->id,
      }
    );
  }

  if ($opts->{fastq_list}) {
    my @lines = io($opts->{fastq_list})->chomp->getlines;
    shift @lines;

    if (scalar @lines == 0) {
      CSG::Mapper::Exceptions::Sample::Fastq::ListEmpty->throw();
    }

    for my $line (@lines) {
      my ($sample_id, $fastq1, $fastq2, @rg) = split(/$TAB/, $line);

      next if $meta->result->sample->has_fastqs($fastq1);

      unless ($sample_id eq $meta->result->sample->sample_id) {
        CSG::Mapper::Exceptions::Sample::FastqMismatch->throw();
      }

      unless (-e $fastq1) {
        CSG::Mapper::Exceptions::Sample::FastqNotFound->throw();
      }

      unless ($rg[0] eq '@RG') {
        CSG::Mapper::Exceptions::Sample::Fastq::MissingRG->throw();
      }

      # XXX - It appears read group headers are not the same across
      #       samples making this test blow up when it shouldn't. might
      #       bring it back if I can pin down these headers more.
      #
      # my $headers = {map {(split(/$COLON/, $_))[0] => 1} @rg[1 .. $#rg]};
      # for (@READ_GROUP_FIELDS) {
      #   unless (exists $headers->{$_}) {
      #     CSG::Mapper::Exceptions::Sample::Fastq::MissingHeader->throw(header => $_);
      #   }
      # }

      $meta->result->sample->add_to_fastqs(
        {
          build      => $meta->result->build,
          path       => $fastq1,
          read_group => join('\t', @rg),
        }
      );

      $logger->info('added fastq ' . $fastq1 . ' to sample ' . $meta->result->sample->sample_id);
    }
  }

  if ($opts->{fastq_complete}) {
    my $fastq = $meta->result->sample->fastqs->search({path => $opts->{fastq_complete}});

    if ($fastq->count > 1) {
      $logger->info('found multiple records for fastq');
      croak 'Found multiple fastq files when there should only be one';
    }

    unless (-e $fastq->first->path) {
      $logger->info('fastq does not exist on disk');
      croak 'unable to locate fastq on disk';
    }

    $logger->info('removing fastq from disk');
    unlink($fastq->first->path);

    $logger->info('removing record from database');
    $fastq->first->delete;
  }
}

1;

__END__

=head1

CSG::Mapper::Command::update - update remapping jobs
{'fraction_non_alphanumeric': 0.20098632176421327, 'fraction_numerical': 0.002977575137247604, 'mean_word_length': 2.88014440433213, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 246, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2256931', 'n_tokens_mistral': 3806, 'n_tokens_neox': 3721, 'n_words': 831}