=head1 NAME

OnlineJudge::Progra - Programming Tasks Grading System
 
=head1 VERSION

Version 0.01

=head1 SYNOPSIS

 use OnlineJudge::Progra;
 
 # Create your soubroutine to obtain new requests to process.
 # Returns requests to be processed.
 sub get {
	 #...
	 return @requests;
 }

 # Create your subroutine to update requests wherever you store them.
 # Receives a processed request.
 sub update {
	 my $request = shift;
	 # ...
 }

 my $judge = OnlineJudge::Progra->new();
 
 $judge->set_home('/home/progra/'); # Progra home directory
 $judge->set_timeinterval(30);		# time in seconds
 
 $judge->run(
	get_sub => sub { &get },
	update_sub => sub { &update }
 );	


=head1 DESCRIPTION

Progra is an Online Judge capable of compile and test programs
written to solve a programming task. 
 
=head1 METHODS

=head2 set_home(/home/foo/bar)

Sets the home directory for Progra files (logs, pid file, etc). If not specified
is set to '/tmp'. When Progra starts, it creates a .pid file in its home directory. 
You should remove the .pid file created in this directory in order to stop Progra.

=head2 set_timeinterval(n)

Time interval (in seconds) it waits to check again for new requests to 
be processed. Default to 60 seconds.

=head2 set_background(true/false)

Defines whether Progra runs on background or not. In case background is
set, then output and errors will be redirected to specific files
found in Progra home directory (output.log and error.log). True by default.

=head2 set_compiler('ext', '/path/to/compiler _SOURCECODE_ -options _BINARY_')

Defines how source codes of a given language must be compiled. The B<_SOURCECODE_>
and B<_BINARY_> strings must be present (yes, with both underscores) and they
would be internally replaced by the corresponding filenames. Example: 

 # how a .c file should be compiled
 $judge->set_compiler('c', '/usr/bin/gcc _SOURCECODE_ -o _BINARY_')
 
There are some compiler strings by default:

 c: /usr/bin/gcc _SOURCECODE_ -o _BINARY_
 cpp: /usr/bin/g++ _SOURCECODE_ -o _BINARY_
 
If you want to avoid the output generated by compilation errors, you should
add '> /dev/null 2>&1' at the end of the string, otherwise it would be redireced
to standard output.

=head2 $judge->set_exec('ext', 'string to execute _FILE_')

Defines how programs of a given language must be executed. The B<_FILE_> string
must be present and it would be internally replaced by the corresponding
filename. Example:

 # how a .pl file should be 'executed'
 $judge->set_exec('pl', '/usr/bin/perl _FILE_')
 
 # how a .c file should be executed
 $judge->set_exec('c', './_FILE_');

There are some execution strings by default:

 pl: /usr/bin/perl _FILE_
 c: ./_FILE_
 cpp: ./_FILE_
 py: /usr/bin/python _FILE_

=head2 diff_options(string)

Replace options for diff command. By default are: 'biwBE'.
The -b argument ignores extra white spaces, the -B ignores white lines, 
the -E ignores tab expansion, the -w ignores all white spaces, the -i 
ignores case differences.

=head2 load_badwords(/path/to/file.txt)

Open the specified file and load the badwords into memory. They must be
separated by commas. This is quite basic and should be enhanced with 
stronger security policies. 

Example: system, exec, popen, etc. 

Be aware that system and system() are different words!. This method is
mandatory.

=head2 verbose(true/false)

Defines if Progra runs in verbose. If true, it prints out every
processed request to standard output. False by default.

=head2 run(get_sub => &get, update_sub => &update)

Runs the judge. It has two mandatory parameters, C<get_sub> and 
C<update_sub>. Both define the subroutines used to obtain new 
requests and to update processed requests.

Subroutines to obtain new requests must return an array
of hash references:

 (
	 {
		 'rid'	=> 1,
		 'sourcecode' => '/home/progra/users/1/task1.pl'
		 'lang' => 'pl',
		 'compile' => 0,
		 'userpath' => '/home/progra/users/1/',
		 'taskpath' => '/home/progra/tasks/1/',
		 'timelimit' => 1,
		 'testcases' => 10,
		 'maxscore'	=> 100,
	 },
	 {
		 'rid'	=> 2,
		 'sourcecode' => '/home/progra/users/2/task1.c',
		 'lang' => 'c',
		 'compile' => 1,
		 'userpath' => '/home/progra/users/2/'
		 'taskpath' => '/home/progra/tasks/1/',
		 'timelimit' => 1,
		 'testcases' => 20,
		 'maxscore' => 100,
	 }
 )
  

You B<MUST> specify a I<rid>, it identifies the processed request uniquelly.

You B<MUST> specify a I<language>. It cannot compile and/or execute a 
source code/program if it does not know its language. The format used is 
according to its source code extension: Perl => pl, C => c, C++ => cpp and
so on. The extension used must be consistent with the ones used in compile
and exec strings.

You B<MUST> specify a I<testcases> number. It has to be consistent with the
test cases you made. If you made ten test cases, they need to be 
named from 0 to 9 with I<input.> and I<output.> prefix, depending on if they 
are input or output files.

If you do not specify a I<maxscore>, it will be 100 by default. This way 
the score obtained for every test case passed will be 100 divided by the
number of test cases.

If you do not specify a I<timelimit>, it will be 1 second by default. 

The compile field specifies if the source code must be compiled or not. False 
if not specified.

You B<MUST> specify a I<sourcecode>, I<userpath> and I<taskpath>. 
Test cases files B<MUST> be inside I<taskpath>. Example:

 -- /home/progra/tasks/1/
 --------------- input.0
 --------------- output.0
 --------------- input.1
 --------------- output.1
 
This represents a directory with I<2> test cases.

Subroutines to update processed requests must receive a hash 
structure like this:

 {
	 'rid' => 1,
	 'grade' => 100,
	 'timemarked' => 1233455,
	 'executiontime' => 0.001,
	 'comment' => 'AC'
 }

The timemarked field is an Unix time stamp, it records the time when the
request was processed. The execution time field is the average execution
time of all test cases. The comment field is a string that represents the
state of the request. The possible values are:

 AC - Accepted: the program passed all test cases.
 WA - Wrong Answer: the program didn't pass all test cases.
 PC - Processing: the source code is in the queue waiting to be processed.
 TL - Time Limit Exceeded: the program ran out of time.
 CE - Compilation Error: there was an error while trying to compile the source code.
 BW - Bad Word found: a forbidden word was found in the source code.
 IE - Internal Error: something went wrong in Progra, check the error.log file.

Note: When a bad/forbidden word is found in the source code or a time limit happens 
when executing it, a directory named "logged" will be created and the source code
that caused the security breach will be saved with the format "requestid_hourmin.ext"

Progra will return the result of a request inmediately after is 
processed, so the update subroutine must receive only one
request at a time. Progra will call this subroutine for each request 
processed.


=head1 BUGS

No bugs found so far.

=head1 AUTHOR

Israel Leiva A. - <ileiva AT csrg DOT cl>

=head1 COPYRIGHT

Copyright (C) 2011-2012 Israel Leiva A.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

http://www.gnu.org/licenses/

=head1 SEE ALSO

L<Time::HiRes>, L<File::Spec::Functions>, L<Proc::Killall>, L<File::Copy>

=cut
