<commit_before>package NonameTV::DataStore::Helper;

use strict;

use Carp;
use NonameTV::Log qw/info progress error logdie/;

=head1 NAME

NonameTV::DataStore::Helper

=head1 DESCRIPTION

Alternative interface to the datastore for NonameTV. Usable for Importers
that receive data where each programme entry does not contain a stop-time
and a date.

=head1 METHODS

=over 4

=cut

=item new

The constructor for the object. Called with a NonameTV::DataStore object
and a timezone-string as parameters. If the timezone is omitted, 
"Europe/Stockholm" is used.

After creating the object, you can set DETECT_SEGMENTS:

    $dsh->{DETECT_SEGMENTS} = 1;

This means that the Datastore::Helper will look for programs that seem
to belong together, i.e. they have been split into two with another
program in between. The algorithm looks for two identical (same title,
description and episode) programs with another program between them.
If such programs are found, they will be marked with the last part
of the episode number as 0/2 and 1/2.

=cut

sub new
{
  my $class = ref( $_[0] ) || $_[0];
  
  my $self = { }; 
  bless $self, $class;
  
  $self->{ds} = $_[1];
  $self->{timezone} = $_[2] || "Europe/Stockholm";

  $self->{DETECT_SEGMENTS} = 0;

  return $self;
}

sub DESTROY
{
  my $self = shift;
  
}

=item StartBatch
  
  Called by an importer to signal the start of a batch of updates.
  Takes two parameters: one containing a string that uniquely identifies
  a set of programmes (a batch_id) and the channel_id for the channel
  that this data is for. The channel_id is a numeric index into the
  channels-table.
  
=cut

sub StartBatch
{
  my $self = shift;
  my( $batch_id, $channel_id ) = @_;
  
  $self->{batch_id} = $batch_id;
  $self->{channel_id} = $channel_id;

  $self->{lasttime} = undef;
  $self->{save_ce} = undef;
  $self->{curr_date} = undef;

  $self->{programs} = [];
  $self->{ds}->StartBatch( $batch_id );
}

=item EndBatch

Called by an importer to signal the end of a batch of updates.
Takes two parameters: 

An integer containing 1 if the batch was processed
successfully, 0 if the batch failed and the database should
be rolled back to the contents as they were before StartBatch was called.
and -1 if the batch should be rolled back because it has not changed.

A string containing a log-message to add to the batchrecord. If success==1,
then the log-message is stored in the field 'message'. If success==0, then
the log-message is stored in abort_message. If success==-1, the log message
is not stored. The log-message can be undef.

=cut

sub EndBatch
{
  my( $self, $success, $log ) = @_;

  if( scalar( @{$self->{programs}} ) > 0 ) {
    $self->CommitPrograms();
  }

  $self->{ds}->EndBatch( $success, $log );
}

sub StartDate
{
  my $self = shift;
  my( $date, $time ) = @_;
  
  if( scalar( @{$self->{programs}} ) > 0 ) {
    $self->CommitPrograms();
  }

#  print "StartDate: $date\n";
  my( $year, $month, $day ) = split( '-', $date );
  $self->{curr_date} = DateTime->new( 
                                      year   => $year,
                                      month  => $month,
                                      day    => $day,
                                      hour   => 0,
                                      minute => 0,
                                      second => 0,
                                      time_zone => $self->{timezone} );

  if( $self->{curr_date} < DateTime->today->subtract( days => 31 ) )
  {
    error( "$self->{batch_id}: StartDate called with old date, " .
           $self->{curr_date}->ymd("-") . "." );
  }
  if( defined( $time ) )
  {
    $self->{lasttime} = $self->create_dt( $self->{curr_date}, $time );
  }
  else
  {
    $self->{lasttime} = undef;
  }

  $self->{programs} = [];
}

=item AddProgramme

Called by an importer to add a programme for the current batch.
Takes a single parameter containing a hashref with information
about the programme. The hashref does NOT need to contain an end_time.

  $dsh->AddProgramme( {
    start_time  => "08:00",
    title       => "Morgon-tv",
    description => "Morgon i TV-soffan",
  } );

=cut

sub AddProgramme
{
  my $self = shift;
  my( $ce ) = @_;

#  print "AddProgramme: $ce->{start_time} $ce->{title}\n";

  if( not defined( $self->{curr_date} ) )
  {
    logdie( "Helper $self->{batch_id}: You must call StartDate before AddProgramme" );
  }

  my $start_time = $self->create_dt( $self->{curr_date}, 
                                     $ce->{start_time} );
  if( defined( $self->{lasttime} ) and ($start_time < $self->{lasttime}) )
  {
    my $new_start_time = $start_time->clone();
    $new_start_time->add( days => 1 );
    my $dur =  $new_start_time - $self->{lasttime};
    my( $days, $hours ) = $dur->in_units( 'days', 'hours' );
    $hours += $days*24;
    if( $hours < 20 )
    {
      # By adding one day to the start_time, we ended up with a time<commit_msg>Fix bug during shift to DST.<commit_after>package NonameTV::DataStore::Helper;

use strict;

use Carp;
use NonameTV::Log qw/info progress error logdie/;

=head1 NAME

NonameTV::DataStore::Helper

=head1 DESCRIPTION

Alternative interface to the datastore for NonameTV. Usable for Importers
that receive data where each programme entry does not contain a stop-time
and a date.

=head1 METHODS

=over 4

=cut

=item new

The constructor for the object. Called with a NonameTV::DataStore object
and a timezone-string as parameters. If the timezone is omitted, 
"Europe/Stockholm" is used.

After creating the object, you can set DETECT_SEGMENTS:

    $dsh->{DETECT_SEGMENTS} = 1;

This means that the Datastore::Helper will look for programs that seem
to belong together, i.e. they have been split into two with another
program in between. The algorithm looks for two identical (same title,
description and episode) programs with another program between them.
If such programs are found, they will be marked with the last part
of the episode number as 0/2 and 1/2.

=cut

sub new
{
  my $class = ref( $_[0] ) || $_[0];
  
  my $self = { }; 
  bless $self, $class;
  
  $self->{ds} = $_[1];
  $self->{timezone} = $_[2] || "Europe/Stockholm";

  $self->{DETECT_SEGMENTS} = 0;

  return $self;
}

sub DESTROY
{
  my $self = shift;
  
}

=item StartBatch
  
  Called by an importer to signal the start of a batch of updates.
  Takes two parameters: one containing a string that uniquely identifies
  a set of programmes (a batch_id) and the channel_id for the channel
  that this data is for. The channel_id is a numeric index into the
  channels-table.
  
=cut

sub StartBatch
{
  my $self = shift;
  my( $batch_id, $channel_id ) = @_;
  
  $self->{batch_id} = $batch_id;
  $self->{channel_id} = $channel_id;

  $self->{lasttime} = undef;
  $self->{save_ce} = undef;
  $self->{curr_date} = undef;

  $self->{programs} = [];
  $self->{ds}->StartBatch( $batch_id );
}

=item EndBatch

Called by an importer to signal the end of a batch of updates.
Takes two parameters: 

An integer containing 1 if the batch was processed
successfully, 0 if the batch failed and the database should
be rolled back to the contents as they were before StartBatch was called.
and -1 if the batch should be rolled back because it has not changed.

A string containing a log-message to add to the batchrecord. If success==1,
then the log-message is stored in the field 'message'. If success==0, then
the log-message is stored in abort_message. If success==-1, the log message
is not stored. The log-message can be undef.

=cut

sub EndBatch
{
  my( $self, $success, $log ) = @_;

  if( scalar( @{$self->{programs}} ) > 0 ) {
    $self->CommitPrograms();
  }

  $self->{ds}->EndBatch( $success, $log );
}

sub StartDate
{
  my $self = shift;
  my( $date, $time ) = @_;
  
  if( scalar( @{$self->{programs}} ) > 0 ) {
    $self->CommitPrograms();
  }

#  print "StartDate: $date\n";
  my( $year, $month, $day ) = split( '-', $date );
  $self->{curr_date} = DateTime->new( 
                                      year   => $year,
                                      month  => $month,
                                      day    => $day,
                                      hour   => 0,
                                      minute => 0,
                                      second => 0,
                                      time_zone => $self->{timezone} );

  if( $self->{curr_date} < DateTime->today->subtract( days => 31 ) )
  {
    error( "$self->{batch_id}: StartDate called with old date, " .
           $self->{curr_date}->ymd("-") . "." );
  }
  if( defined( $time ) )
  {
    $self->{lasttime} = $self->create_dt( $self->{curr_date}, $time );
  }
  else
  {
    $self->{lasttime} = undef;
  }

  $self->{programs} = [];
}

=item AddProgramme

Called by an importer to add a programme for the current batch.
Takes a single parameter containing a hashref with information
about the programme. The hashref does NOT need to contain an end_time.

  $dsh->AddProgramme( {
    start_time  => "08:00",
    title       => "Morgon-tv",
    description => "Morgon i TV-soffan",
  } );

=cut

sub AddProgramme
{
  my $self = shift;
  my( $ce ) = @_;

#  print "AddProgramme: $ce->{start_time} $ce->{title}\n";

  if( not defined( $self->{curr_date} ) )
  {
    logdie( "Helper $self->{batch_id}: You must call StartDate before AddProgramme" );
  }

  my $start_time = $self->create_dt( $self->{curr_date}, 
                                     $ce->{start_time} );
  if( defined( $self->{lasttime} ) and ($start_time < $self->{lasttime}) )
  {
    my $new_start_time = $start_time->clone();

    # We cannot use days => 1 here since it dies if the new date
    # is invalid due to a DST change.
    $new_start_time->add( hours => 24 );

    my $dur =  $new_start_time - $self->{lasttime};
    my( $days, $hours ) = $dur->in_units( 'days', 'hours' );
    $hours += $days*24;
    if( $hours < 20 )
    {
      # By adding one day to the start_time, we ended up with a time
{'fraction_non_alphanumeric': 0.12891566265060242, 'fraction_numerical': 0.007429718875502008, 'mean_word_length': 2.658097686375321, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 124, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2247602', 'n_tokens_mistral': 3414, 'n_tokens_neox': 3265, 'n_words': 1113}