#!/usr/bin/perl -w

use FindBin;
use lib "$FindBin::Bin/../perl_lib";

######################################################################
#
#
######################################################################

=pod

=for Pod2Wiki

=head1 NAME

B<run_staged_tasks> - Runs staged tasks from the event queue

=head1 SYNOPSIS

B<run_staged_tasks> I<repository_id> [B<options>]

=head1 DESCRIPTION

Sometimes tasks in the event queue can take a long time to run and will fail because they expire after the indexer timeout (default: 10 minutes).  Although increasing the indexer timeout may help with this issue, it can be difficult to determine a suitable timeout that will allow tasks time to complete, whilst not causing too much lag between tasks being added to the event queue and being processed.

New Event plugins can be created or existing ones extended to set newly created tasks as 'staged'.  Rather than these tasks being executed by the indexer will just be logged in the event queue.  This script can then used to execute these 'staged' tasks with an infinite timeout.  This script can be run as a cron job and will not run if it is already running, save running the same tasks simultaneously.  The script can be restricted by the Event plugin and/or its actions, so only certain staged tasks are executed.

=head1 ARGUMENTS

=over 8

=item B<repository_id>

The ID of the EPrints repository to use.

=back

=head1 OPTIONS

=over 8

=item B<--action=s>

Only run staged tasks for a particular event action.  Sensibly to run in combination with --plugin to ensure only the tasks you expect are run.

=item B<--help>

Print a brief help message and exit.

=item B<--man>

Print the full manual page and then exit.

=item B<--plugin=s>

Only run staged tasks for a particular event plugin.

=item B<--quiet>

This option will suppress all output unless an error occurs.

=item B<--verbose>

Explain in detail what is going on.

=item B<--version>

Output version information and exit.

=back

=cut

use EPrints;

use strict;
use warnings;

use Getopt::Long;
use Pod::Usage;
use Data::Dumper;

my $version = 0;
my $verbose = 0;
my $quiet = 0;
my $help = 0;
my $man = 0;
my $plugin_opt;
my $action_opt;

Getopt::Long::Configure("permute");

GetOptions(
    'help|?' => \$help,
    'man' => \$man,
    'version' => \$version,
    'verbose+' => \$verbose,
    'silent' => \$quiet,
    'quiet' => \$quiet,
    'plugin=s' => \$plugin_opt,
    'action=s' => \$action_opt,
) || pod2usage( 2 );
EPrints::Utils::cmd_version( "run_staged_tasks" ) if $version;
pod2usage( 1 ) if $help;
pod2usage( -exitstatus => 0, -verbose => 2 ) if $man;
pod2usage( 2 ) if( scalar @ARGV != 1 );

my $repoid = shift @ARGV;
pod2usage( 1 ) unless defined $repoid;


# Are we already running staged tasks
my $pid;
my $nopidfile = 0;
my $pidfile = EPrints::Config::get("var_path") . "/staged_tasks.pid";
open( PID, "<", $pidfile) or $nopidfile = 1;
if( !$nopidfile )
{

        while(defined($pid = <PID>))
        {
                chomp($pid);
                last if $pid+0 > 0;
        }
        close( PID );
}
if( $nopidfile || !EPrints::Platform::proc_exists( $pid ) )
{
        # Not running - so write new pid to file
        open( PID, ">", $pidfile) or die "Error writing pid file $pidfile: $!";
        print PID ( $$ );
        close( PID );

        my $session = new EPrints::Session( 1, $repoid ) or die("Cannot create Session object");

        # Prepare log file
        my $filename = EPrints::Config::get("var_path") . "/staged_tasks.log";
        open(my $fh, '>>', $filename) or die "Could not open file '$filename' $!";

        # Get indexer events
        my $ds = $session->get_repository->get_dataset("event_queue");
        my $search_exp = $ds->prepare_search();

        #get events which failed
        $search_exp->add_field(
                fields => [ $ds->field( 'status' ) ],
                value => 'staged',
                match => "EQ",
        );

	if ( $plugin_opt ) 
        {
                $search_exp->add_field(
                        fields => [ $ds->field( 'pluginid' ) ],
                        value => $plugin_opt,
                        match => "EQ",
                );
        }

	if ( $action_opt ) 
	{
	        $search_exp->add_field(
        	        fields => [ $ds->field( 'action' ) ],
                	value => $action_opt,
	                match => "EQ",
	        );
	}

    my $list = $search_exp->perform_search;

	print "Found " . $list->count . " staged task(s) that meet stated criteria.\n" if $verbose;

	# Loop through staged tasks in the indexer
	my $success = 0;
	$list->map( sub{
		my($session, $dataset, $event) = @_;

		my $staged_task = $event->value( "pluginid" )."->".$event->value( "action" )." with parameters: [ '" . join( "', '", @{ $event->value( "params" ) } ) . "' ]";

		print "Running staged task: $staged_task\n" if $verbose;
		my $rc = $event->_execute;
		if ( $rc != EPrints::Const::HTTP_OK )
		{
			if ( $rc == EPrints::Const::HTTP_INTERNAL_SERVER_ERROR )
			{
				$event->set_value( 'status', 'failed' );
				$event->commit;
			}
			elsif ( $rc == EPrints::Const::HTTP_NOT_FOUND )
			{
				$event->remove;
			}
		}
		else
		{
			$event->remove;
			$success++;
			print "Completed staged task: $staged_task\n" if $verbose;
		}
	} );

	print $list->count . " staged task(s) that meet stated criteria have been executed of which $success succeeded.\n" if $verbose;

	$session->terminate;
	
	unlink($pidfile);
}
else
{
	print "Could not run as already running as a separate process.  See $pidfile\n";
	exit(1);
}

=head1 COPYRIGHT

=for COPYRIGHT BEGIN

Copyright 2023 University of Southampton.
EPrints 3.4 is supplied by EPrints Services.

http://www.eprints.org/eprints-3.4/

=for COPYRIGHT END

=for LICENSE BEGIN

This file is part of EPrints 3.4 L<http://www.eprints.org/>.

EPrints 3.4 and this file are released under the terms of the
GNU Lesser General Public License version 3 as published by
the Free Software Foundation unless otherwise stated.

EPrints 3.4 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with EPrints 3.4.
If not, see L<http://www.gnu.org/licenses/>.

=for LICENSE END

