X-Git-Url: http://git.ieval.ro/?a=blobdiff_plain;f=lib%2FGruntmaster%2FData.pm;h=635719422c5fc427d52e7fca02b6329f0010da82;hb=f7386aabf2077e1067b14cdcd5162e8c9b762bc6;hp=48de966aed29da3093ccc14d92e73ac97ea0b1e6;hpb=bbf8209c979ab3d89e93e13117dd4b9f639dba9c;p=gruntmaster-data.git diff --git a/lib/Gruntmaster/Data.pm b/lib/Gruntmaster/Data.pm index 48de966..6357194 100644 --- a/lib/Gruntmaster/Data.pm +++ b/lib/Gruntmaster/Data.pm @@ -1,85 +1,470 @@ package Gruntmaster::Data; - -use 5.014002; -use strict; +use v5.14; use warnings; +use parent qw/Exporter/; -require Exporter; - -our @ISA = qw(Exporter); - -# 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. - -# 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( - -) ] ); - -our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); - -our @EXPORT = qw( - -); +use JSON qw/encode_json decode_json/; +use Redis; +use Sub::Name qw/subname/; our $VERSION = '5999.000_001'; -$VERSION = eval $VERSION; # see L - -# Preloaded methods go here. +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 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 statement owner author/; +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 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__ . '::'}; +}; 1; __END__ -# Below is stub documentation for your module. You'd better edit it! + +=encoding utf-8 =head1 NAME -Gruntmaster::Data - Perl extension for blah blah blah +Gruntmaster::Data - Gruntmaster 6000 Online Judge -- database interface and tools =head1 SYNOPSIS - use Gruntmaster::Data; - blah blah blah + for my $problem (problems) { + say "Problem name: " . problem_name $problem; + say "Problem level: " . problem_level $problem; + ... + } =head1 DESCRIPTION -Stub documentation for Gruntmaster::Data, created by h2xs. It looks like the -author of the extension was negligent enough to leave the stub -unedited. +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. + +The current contest is selected by setting the C<< $Gruntmaster::Data::contest >> variable. + + local $Gruntmaster::Data::contest = 'mycontest'; + say 'There are' . jobcard . ' jobs in my contest'; + +=head1 FUNCTIONS + +=head2 Redis + +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. + +These functions correspond to Redis commands. The current list is: B<< MULTI EXEC SMEMBERS GET HGET HDEL HSET SADD SREM INCR HMSET HSETNX DEL PUBLISH SUBSCRIBE WAIT_FOR_MESSAGES >>. + +=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 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>, 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