<commit_before>=head1 LICENSE

Copyright [1999-2014] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=cut

package EnsEMBL::Web::File;

use strict;

use EnsEMBL::Web::Utils::RandomString qw(random_string);

use EnsEMBL::Web::File::Utils::IO qw/:all/;
use EnsEMBL::Web::File::Utils::Memcached qw/:all/;

### Replacement for EnsEMBL::Web::TmpFile, using the file-handling
### functionality in EnsEMBL::Web::File::Utils 

### Data can be written to disk or, if enabled and appropriate, memcached
### Note that to aid cleanup, all files written to disk should use a common
### path pattern, as follows:
### /base_dir/timestamp/user_identifier/sub_dir/file_name.ext
###  - base_dir is set in subclasses - this is the main temporary file location
###  - timestamp aids in cleaning up older files by date
###  - user_identifier is either session id or user id, and 
###    helps to ensure that users only see their own data
###  - sub_dir is optional - it's used by a few pages to separate content further
###  - file_name may be auto-generated, or set by the user

sub new {
### @constructor
### N.B. You need either the path (to an existing file) or
### the name and extension (for new files)
### @param Hash of arguments 
###  - hub - for getting TMP_DIR location, etc
###  - file_path - full path to file (optional)
###  - name (optional) String - not including file extension
###  - extension (optional) String
###  - compression (optional) String
### @return EnsEMBL::Web::File
  my ($class, %args) = @_;
  my $self = \%args;

  ## Set base locations
  $args{'base_dir'} ||= $self->{'hub'}->species_defs->ENSEMBL_TMP_DIR;
  $args{'base_url'} ||= $self->{'hub'}->species_defs->ENSEMBL_TMP_URL;

  ## Set default driver (disk only)
  $args{'drivers'} ||= ['IO'];

  $self->{'error'} = undef;
  bless $self, $class;

  my $file_path = $self->{'file_path'};

  if ($file_path) {
    ## DEALING WITH AN EXISTING FILE
   
    ## Clean up the path
    $file_path =~ s/$self->{'hub'}->species_defs->ENSEMBL_TMP_DIR//;
    $file_path =~ s/$self->{'hub'}->species_defs->ENSEMBL_TMP_URL//;
    $self->{'file_path'} = $file_path;

    my @path = grep length, split('/', $file_path);

    ## Parse filename
    $self->{'file_name'}     = pop @path;
    my ($name, $extension, $compression) = split(/\./, $self->{'file_name'});
    $compression =~ s/2$//; ## We use 'bz' internally, not 'bz2'
    $self->{'name'}         = $name;
    $self->{'extension'}    = $extension;
    $self->{'compression'}  = $compression;
    $self->{'compress'}     = $self->{'compression'} ? 1 : 0;

    ## Parse rest of path
    $self->{'dir_path'}         = join('/', @path); 
    $self->{'timestamp'}        = shift @path;
    $self->{'user_identifier'}  = shift @path;
    $self->{'sub_dir'}          = shift @path if scalar @path;
  }
  else {
    ## CREATING A NEW FILE (or trying to...)
    if ($self->{'name'}) {
      ## Make sure it's a valid file name!
      $self->{'name'} =~ s/[^\w]/_/g;
      ## Set a random path in case we have multiple files with this name
      $self->{'sub_dir'} ||= random_string;
    }
    else {
      ## Create a file name if none given
      $self->{'name'} = random_string;
    }

    $self->{'extension'} ||= 'txt';
    ## Allow for atypical file extensions such as gff3 or bedGraph
    (my $extension          = $self->{'extension'}) =~ s/^\.?(\w+)(\.gz)?$/$1/;
    $self->{'extension'}    = $extension;

    my $file_name            = $self->{'name'}.'.'.$extension;

    if ($self->{'compress'}) {
      unless ($self->{'compression'}) {
        ## Default to gzip
        $self->{'compression'} = 'gz';
      }
      $file_name .= '.'.$self->{'compression'};
    }

    $self->{'file_name'} = $file_name;

    my @path_elements = ($self->set_timestamp, $self->set_user_identifier);
    push @path_elements, $self->{'sub_dir'} if $self->{'sub_dir'};
    $self->{'dir_path'} = join('/', @path_elements); 

    push @path_elements, $file_name;
    $self->{'file_path'} = join('/', @path_elements); 
  }
  $self->{'base_path'}    = $self->{'base_dir'}.'/'.$self->{'dir_path'}; 
  $self->{'location'}     = $self->{'base_dir'}.'/'.$self->{'file_path'}; 
  $self->{'url'}          = $self->{'base_url'}.'/'.$self->{'file_path'}; 

  return $self;
}

sub file_name {
### a
  my $self = shift;
  return $self->{'file_name'};
}

sub extension {
### a
  my $self = shift;
  return $self->{'extension'};
}

sub compression {
### a
  my $self = shift;
  return $self->{'compression'};
}

sub compress {
### a
  my $self = shift;
  return $self->{'compress'};
}

sub timestamp {
### a
  my $self = shift;
  return $self->{'timestamp'};
}

sub user_identifier {
### a
  my $self = shift;
  return $self->{'user_identifier'};
}

sub file_path {
### a
  my $self = shift;
  return $self->{'file_path'};
}

sub sub_dir {
### a
  my $self = shift;
  return $self->{'sub_dir'};
}

sub base_path {
### a
  my $self = shift;
  return $self->{'base_path'};
}

sub base_dir {
### a
  my $self = shift;
  return $self->{'base_dir'};
}

sub location {
### a
  my $self = shift;
  return $self->{'location'};
}

sub url {
### a
  my $self = shift;
  return $self->{'url'};
}

sub hub {
### a
  my $self = shift;
  return $self->{'hub'};
}

sub error {
### a
  my $self = shift;
  return $self->{'error'};
}

sub set_timestamp {
  ### a
  my $self = shift;
  
  my @time  = localtime;
  my $day   = $time[3];
  my $month = $time[4] + 1;
  my $year  = $time[5] + 1900;

  $self->{'timestamp'} = sprintf('%s_%s_%s', $year, $month, $day);
  return $self->{'timestamp'};<commit_msg>Rename timestamp variable/method to datestamp, and use 'timestamp' to refer to an actual time that can optionally used in autogenerated file names (e.g. used with dynamic image files in addition to random string, to ensure uniqueness)<commit_after>=head1 LICENSE

Copyright [1999-2014] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=cut

package EnsEMBL::Web::File;

use strict;

use EnsEMBL::Web::Utils::RandomString qw(random_string);

use EnsEMBL::Web::File::Utils::IO qw/:all/;
use EnsEMBL::Web::File::Utils::Memcached qw/:all/;

### Replacement for EnsEMBL::Web::TmpFile, using the file-handling
### functionality in EnsEMBL::Web::File::Utils 

### Data can be written to disk or, if enabled and appropriate, memcached
### Note that to aid cleanup, all files written to disk should use a common
### path pattern, as follows:
### /base_dir/datestamp/user_identifier/sub_dir/file_name.ext
###  - base_dir is set in subclasses - this is the main temporary file location
###  - datestamp aids in cleaning up older files by date
###  - user_identifier is either session id or user id, and 
###    helps to ensure that users only see their own data
###  - sub_dir is optional - it's used by a few pages to separate content further
###  - file_name may be auto-generated, or set by the user

sub new {
### @constructor
### N.B. You need either the path (to an existing file) or
### the name and extension (for new files)
### @param Hash of arguments 
###  - hub - for getting TMP_DIR location, etc
###  - file_path - full path to file (optional)
###  - name (optional) String - not including file extension
###  - extension (optional) String
###  - compression (optional) String
### @return EnsEMBL::Web::File
  my ($class, %args) = @_;
  my $self = \%args;

  ## Set base locations
  $args{'base_dir'} ||= $self->{'hub'}->species_defs->ENSEMBL_TMP_DIR;
  $args{'base_url'} ||= $self->{'hub'}->species_defs->ENSEMBL_TMP_URL;

  ## Set default driver (disk only)
  $args{'drivers'} ||= ['IO'];

  $self->{'error'} = undef;
  bless $self, $class;

  my $file_path = $self->{'file_path'};

  if ($file_path) {
    ## DEALING WITH AN EXISTING FILE
   
    ## Clean up the path
    $file_path =~ s/$self->{'hub'}->species_defs->ENSEMBL_TMP_DIR//;
    $file_path =~ s/$self->{'hub'}->species_defs->ENSEMBL_TMP_URL//;
    $self->{'file_path'} = $file_path;

    my @path = grep length, split('/', $file_path);

    ## Parse filename
    $self->{'file_name'}     = pop @path;
    my ($name, $extension, $compression) = split(/\./, $self->{'file_name'});
    $compression =~ s/2$//; ## We use 'bz' internally, not 'bz2'
    $self->{'name'}         = $name;
    $self->{'extension'}    = $extension;
    $self->{'compression'}  = $compression;
    $self->{'compress'}     = $self->{'compression'} ? 1 : 0;

    ## Parse rest of path
    $self->{'dir_path'}         = join('/', @path); 
    $self->{'datestamp'}        = shift @path;
    $self->{'user_identifier'}  = shift @path;
    $self->{'sub_dir'}          = shift @path if scalar @path;
  }
  else {
    ## CREATING A NEW FILE (or trying to...)
    if ($self->{'name'}) {
      ## Make sure it's a valid file name!
      $self->{'name'} =~ s/[^\w]/_/g;
      ## Set a random path in case we have multiple files with this name
      $self->{'sub_dir'} ||= random_string;
    }
    else {
      ## Create a file name if none given
      $self->{'name'} = $self->set_timestamp if $args{'name_timestamp'};
      $self->{'name'} .= random_string;
    }

    $self->{'extension'} ||= 'txt';
    ## Allow for atypical file extensions such as gff3 or bedGraph
    (my $extension          = $self->{'extension'}) =~ s/^\.?(\w+)(\.gz)?$/$1/;
    $self->{'extension'}    = $extension;

    my $file_name            = $self->{'name'}.'.'.$extension;

    if ($self->{'compress'}) {
      unless ($self->{'compression'}) {
        ## Default to gzip
        $self->{'compression'} = 'gz';
      }
      $file_name .= '.'.$self->{'compression'};
    }

    $self->{'file_name'} = $file_name;

    my @path_elements = ($self->set_datestamp, $self->set_user_identifier);
    push @path_elements, $self->{'sub_dir'} if $self->{'sub_dir'};
    $self->{'dir_path'} = join('/', @path_elements); 

    push @path_elements, $file_name;
    $self->{'file_path'} = join('/', @path_elements); 
  }
  $self->{'base_path'}    = $self->{'base_dir'}.'/'.$self->{'dir_path'}; 
  $self->{'location'}     = $self->{'base_dir'}.'/'.$self->{'file_path'}; 
  $self->{'url'}          = $self->{'base_url'}.'/'.$self->{'file_path'}; 

  return $self;
}

sub file_name {
### a
  my $self = shift;
  return $self->{'file_name'};
}

sub extension {
### a
  my $self = shift;
  return $self->{'extension'};
}

sub compression {
### a
  my $self = shift;
  return $self->{'compression'};
}

sub compress {
### a
  my $self = shift;
  return $self->{'compress'};
}

sub datestamp {
### a
  my $self = shift;
  return $self->{'datestamp'};
}

sub user_identifier {
### a
  my $self = shift;
  return $self->{'user_identifier'};
}

sub file_path {
### a
  my $self = shift;
  return $self->{'file_path'};
}

sub sub_dir {
### a
  my $self = shift;
  return $self->{'sub_dir'};
}

sub base_path {
### a
  my $self = shift;
  return $self->{'base_path'};
}

sub base_dir {
### a
  my $self = shift;
  return $self->{'base_dir'};
}

sub location {
### a
  my $self = shift;
  return $self->{'location'};
}

sub url {
### a
  my $self = shift;
  return $self->{'url'};
}

sub hub {
### a
  my $self = shift;
  return $self->{'hub'};
}

sub error {
### a
  my $self = shift;
  return $self->{'error'};
}

sub set_timestamp {
### Create a timestamp as part of a filename
  my $self = shift;

  my @time  = localtime;
  my $hour  = $time[2];
  my $min   = $time[1];
  my $sec   = $time[0];

  $self->{'name'} = sprintf('%s%s%s', $hour, $min, $sec);
  return $self->{'name'};
}

sub set_datestamp {
  ### a
  my $self = shift;
  
  my @time  = localtime;
  my $day   = $time[3];
  my $month = $time[4] + 1;
  my $year  = $time[5] + 1900;

  $self->{'datestamp'} = sprintf('%s_%s_%s', $year, $month, $day);
  return $self->{'datestamp'};
{'fraction_non_alphanumeric': 0.1858213616489694, 'fraction_numerical': 0.004450343535290444, 'mean_word_length': 3.3171553758004717, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 147, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '489270', 'n_tokens_mistral': 4509, 'n_tokens_neox': 4361, 'n_words': 1272}