X-Git-Url: http://git.ieval.ro/?a=blobdiff_plain;f=lib%2FGruntmaster%2FData.pm;h=da9d944c5e8c5e799f34ec140d75dc61118748bb;hb=7625bb863c3684441677052636abf78039f6d055;hp=48de966aed29da3093ccc14d92e73ac97ea0b1e6;hpb=bbf8209c979ab3d89e93e13117dd4b9f639dba9c;p=gruntmaster-data.git diff --git a/lib/Gruntmaster/Data.pm b/lib/Gruntmaster/Data.pm index 48de966..da9d944 100644 --- a/lib/Gruntmaster/Data.pm +++ b/lib/Gruntmaster/Data.pm @@ -1,85 +1,542 @@ package Gruntmaster::Data; - -use 5.014002; -use strict; +use v5.14; use warnings; +use parent qw/Exporter/; + +use JSON qw/encode_json decode_json/; +use Redis; +use Sub::Name qw/subname/; + +our $VERSION = '5999.000_002'; + +our $contest; +my $redis = Redis->new; +my $pubsub = Redis->new; + +sub dynsub{ + our ($name, $sub) = @_; + no strict 'refs'; + *$name = subname $name => $sub +} + +BEGIN { + for my $cmd (qw/multi exec smembers get hget hgetall hdel hset sadd srem incr hmset hsetnx publish del/) { + dynsub uc $cmd, sub { $redis->$cmd(@_) }; + } + + for my $cmd (qw/subscribe wait_for_messages/) { + dynsub uc $cmd, sub { $pubsub->$cmd(@_) }; + } +} + +sub cp { defined $contest ? "contest.$contest." : '' } + +sub problems () { SMEMBERS cp . 'problem' } +sub contests () { SMEMBERS cp . 'contest' } +sub users () { SMEMBERS cp . 'user' } +sub jobcard () { GET cp . 'job' } + +sub job_results (_) { decode_json HGET cp . "job.$_[0]", 'results' } +sub set_job_results ($+) { HSET cp . "job.$_[0]", 'results', encode_json $_[1] } +sub job_inmeta (_) { decode_json HGET cp . "job.$_[0]", 'inmeta' } +sub set_job_inmeta ($+) { HSET cp . "job.$_[0]", 'inmeta', encode_json $_[1] } +sub problem_meta (_) { decode_json HGET cp . "problem.$_[0]", 'meta' } +sub set_problem_meta ($+) { HSET cp . "problem.$_[0]", 'meta', encode_json $_[1] } +sub job_daemon (_) { HGET cp . "job.$_[0]", 'daemon' } +sub set_job_daemon ($$) { HSETNX cp . "job.$_[0]", 'daemon', $_[1] }; + +sub defhash{ + my ($name, @keys) = @_; + for my $key (@keys) { + dynsub "${name}_$key", sub (_) { HGET cp . "$name.$_[0]", $key }; + dynsub "set_${name}_$key", sub ($$) { HSET cp . "$name.$_[0]", $key, $_[1] }; + } + + dynsub "edit_$name", sub { + my ($key, %values) = @_; + HMSET cp . "$name.$key", %values; + }; + + dynsub "insert_$name", sub { + my ($key, %values) = @_; + SADD cp . $name, $key or return; + HMSET cp . "$name.$key", %values; + }; + dynsub "remove_$name", sub (_) { + my $key = shift; + SREM cp . $name, $key; + DEL cp . "$name.$key"; + }; + + dynsub "push_$name", sub { + my $nr = INCR cp . $name; + HMSET cp . "$name.$nr", @_; + $nr + }; +} + +defhash problem => qw/name level difficulty statement owner author private generator runner judge testcnt timeout olimit/; +defhash contest => qw/start end name owner/; +defhash job => qw/date errors extension filesize private problem result result_text user/; +defhash user => qw/name email lastjob town university level/; + +sub clean_job (_){ + HDEL cp . "job.$_[0]", qw/result result_text results daemon/ +} + +sub mark_open { + my ($problem, $user) = @_; + HSETNX cp . 'open', "$problem.$user", time; +} + +sub get_open { + my ($problem, $user) = @_; + HGET cp . 'open', "$problem.$user"; +} + +our @EXPORT = do { + no strict 'refs'; + grep { $_ =~ /^[a-zA-Z]/ and exists &$_ } keys %{__PACKAGE__ . '::'}; +}; -require Exporter; +1; +__END__ -our @ISA = qw(Exporter); +=encoding utf-8 -# Items to export into callers namespace by default. Note: do not export -# names by default without a very good reason. Use EXPORT_OK instead. -# Do not simply export all your public functions/methods/constants. +=head1 NAME -# This allows declaration use Gruntmaster::Data ':all'; -# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK -# will save memory. -our %EXPORT_TAGS = ( 'all' => [ qw( - -) ] ); +Gruntmaster::Data - Gruntmaster 6000 Online Judge -- database interface and tools -our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +=head1 SYNOPSIS -our @EXPORT = qw( - -); + for my $problem (problems) { + say "Problem name: " . problem_name $problem; + say "Problem level: " . problem_level $problem; + ... + } -our $VERSION = '5999.000_001'; -$VERSION = eval $VERSION; # see L +=head1 DESCRIPTION +Gruntmaster::Data is the Redis interface used by the Gruntmaster 6000 Online Judge. It exports many functions for talking to the database. All functions are exported by default. -# Preloaded methods go here. +The current contest is selected by setting the C<< $Gruntmaster::Data::contest >> variable. -1; -__END__ -# Below is stub documentation for your module. You'd better edit it! + local $Gruntmaster::Data::contest = 'mycontest'; + say 'There are' . jobcard . ' jobs in my contest'; -=head1 NAME +=head1 FUNCTIONS -Gruntmaster::Data - Perl extension for blah blah blah +=head2 Redis -=head1 SYNOPSIS +Gruntmaster::Data exports some functions for talking directly to the Redis server. These functions should not normally be used, except for B, B, B, B and B. - use Gruntmaster::Data; - blah blah blah +These functions correspond to Redis commands. The current list is: B<< MULTI EXEC SMEMBERS GET HGET HGETALL HDEL HSET SADD SREM INCR HMSET HSETNX DEL PUBLISH SUBSCRIBE WAIT_FOR_MESSAGES >>. -=head1 DESCRIPTION +=head2 Problems + +=over + +=item B + +Returns a list of problems in the current contest. + +=item B I<$problem> + +Returns a problem's meta. + +=item B I<$problem>, I<$meta> + +Sets a problem's meta. + +=item B I<$problem> + +Returns a problem's name. + +=item B I<$problem>, I<$name> + +Sets a problem's name. + +=item B I<$problem> + +Returns a problem's level. The levels are beginner, easy, medium, hard. + +=item B I<$problem>, I<$level> + +Sets a problem's level. The levels are beginner, easy, medium, hard. + +=item B I<$problem> + +Returns a problem's difficulty. + +=item B I<$problem>, I<$name> + +Sets a problem's difficulty. + +=item B I<$problem> + +Returns a problem's statement. + +=item B I<$problem>, I<$statement> + +Sets a problem's statement. + +=item B I<$problem> + +Returns a problem's owner. + +=item B I<$problem>, I<$owner> + +Sets a problem's owner. + +=item B I<$problem> + +Returns a problem's author. + +=item B I<$problem>, I<$author> + +Sets a problem's author. + +=item B I<$problem> + +Returns a problem's private flag (true if the problem is private, false otherwise). + +=item B I<$problem>, I<$private> + +Sets a problem's private flag. + +=item B I<$problem> + +Returns a problem's generator. The generators are File, Run and Undef. More might be added in the future. + +=item B I<$problem>, I<$generator> + +Sets a problem's generator. + +=item B I<$problem> + +Returns a problem's runner. The runners are File, Verifier and Interactive. More might be added in the future. + +=item B I<$problem>, I<$runner> + +Sets a problem's runner. + +=item B I<$problem> + +Returns a problem's judge. The judges are Absolute and Points. More might be added in the future. + +=item B I<$problem>, I<$judge> + +Sets a problem's judge. + +=item B I<$problem> + +Returns a problem's test count. + +=item B I<$problem>, I<$testcnt> + +Sets a problem's test count. + +=item B I<$problem> + +Returns a problem's time limit, in seconds. + +=item B I<$problem>, I<$timeout> + +Sets a problem's time limit, in seconds. + +=item B I<$problem> + +Returns a problem's output limit, in bytes. + +=item B I<$problem>, I<$olimit> + +Sets a problem's output limit, in bytes. + +=item B I<$problem>, I<$user> + +Returns the time when I<$user> opened I<$problem>. + +=item B I<$problem>, I<$user> + +Sets the time when I<$user> opened I<$problem> to the current time. Does nothing if I<$user> has already opened I<$problem>. + +=item B I<$id>, I<$key> => I<$value>, ... + +Inserts a problem with id I<$id> and the given initial configuration. Does nothing if a problem with id I<$id> already exists. Returns true if the problem was added, false otherwise. + +=item B I<$id>, I<$key> => I<$value>, ... + +Updates the configuration of a problem. The values of the given keys are updated. All other keys/values are left intact. + +=item B I<$id> + +Removes a problem. + +=back + +=head2 Contests + +B<<< WARNING: these functions only work correctly when C<< $Gruntmaster::Data::contest >> is undef >>> + +=over + +=item B + +Returns a list of contests. + +=item B I<$contest> + +Returns a contest's start time. + +=item B I<$contest>, I<$start> + +Sets a contest's start time. + +=item B I<$contest> + +Returns a contest's end time. + +=item B I<$contest>, I<$end> + +Sets a contest's end time. + +=item B I<$contest> + +Returns a contest's name. + +=item B I<$contest>, I<$name> + +Sets a contest's name. + +=item B I<$contest> + +Returns a contest's owner. + +=item B I<$contest>, I<$owner> + +Sets a contest's owner. + +=item B I<$id>, I<$key> => I<$value>, ... + +Inserts a contest with id I<$id> and the given initial configuration. Does nothing if a contest with id I<$id> already exists. Returns true if the contest was added, false otherwise. + +=item B I<$id>, I<$key> => I<$value>, ... + +Updates the configuration of a contest. The values of the given keys are updated. All other keys/values are left intact. + +=item B I<$id> + +Removes a contest. + +=back + +=head2 Jobs + +=over + +=item B + +Returns the number of jobs in the database. + +=item B I<$job> + +Returns an array of job results. Each element corresponds to a test and is a hashref with keys B (test number), B (result code, see L), B (result description) and B