Add problem_status, contest_status and a method that updates them
authorMarius Gavrilescu <marius@ieval.ro>
Fri, 12 Dec 2014 11:22:32 +0000 (13:22 +0200)
committerMarius Gavrilescu <marius@ieval.ro>
Fri, 12 Dec 2014 11:22:32 +0000 (13:22 +0200)
db.sql
lib/Gruntmaster/Data.pm
lib/Gruntmaster/Data/Result/Contest.pm
lib/Gruntmaster/Data/Result/ContestStatus.pm [new file with mode: 0644]
lib/Gruntmaster/Data/Result/Job.pm
lib/Gruntmaster/Data/Result/Problem.pm
lib/Gruntmaster/Data/Result/ProblemStatus.pm [new file with mode: 0644]
lib/Gruntmaster/Data/Result/User.pm

diff --git a/db.sql b/db.sql
index 1556202076909318c6718fc10d3189caeac78a28..696cec175e2f0a3d5d7574dae80cee94cc271897 100644 (file)
--- a/db.sql
+++ b/db.sql
@@ -20,6 +20,15 @@ CREATE TABLE contests (
        CONSTRAINT positive_duration CHECK (stop > start)
 );
 
+CREATE TABLE contest_status (
+       contest TEXT NOT NULL REFERENCES contests ON DELETE CASCADE,
+       owner   TEXT NOT NULL REFERENCES users ON DELETE CASCADE,
+       score   INT  NOT NULL,
+       rank    INT  NOT NULL,
+
+       PRIMARY KEY (owner, contest)
+);
+
 CREATE TABLE problems (
        id        TEXT      PRIMARY KEY,
        author    TEXT,
@@ -67,6 +76,15 @@ CREATE TABLE jobs (
        owner       TEXT    NOT NULL REFERENCES users ON DELETE CASCADE
 );
 
+CREATE TABLE problem_status (
+       problem TEXT    NOT NULL REFERENCES problems ON DELETE CASCADE,
+       owner   TEXT    NOT NULL REFERENCES users ON DELETE CASCADE,
+       job     SERIAL  NOT NULL REFERENCES jobs ON DELETE CASCADE,
+       solved  BOOLEAN NOT NULL DEFAULT FALSE,
+
+       PRIMARY KEY (owner, problem)
+);
+
 CREATE TABLE opens (
        contest TEXT   NOT NULL REFERENCES contests ON DELETE CASCADE,
        problem TEXT   NOT NULL REFERENCES problems ON DELETE CASCADE,
index 9cb313d67ca7073e6f92a69b7beb24a99b403894..2422e77e5f0a57d0dce196dbc9921fc3dd124169 100644 (file)
@@ -34,7 +34,7 @@ sub dynsub{
 }
 
 BEGIN {
-       for my $rs (qw/contest contest_problem job open problem user/) {
+       for my $rs (qw/contest contest_problem job open problem user problem_status contest_status/) {
                my $rsname = ucfirst $rs;
                $rsname =~ s/_([a-z])/\u$1/gs;
                dynsub PL_N($rs) => sub { $_[0]->resultset($rsname)              };
@@ -96,7 +96,10 @@ sub user_list {
 
 sub user_entry {
        my ($self, $id) = @_;
-       +{ $self->users->find($id, {columns => USER_PUBLIC_COLUMNS})->get_columns }
+       my $user = $self->users->find($id, {columns => USER_PUBLIC_COLUMNS, prefetch => [qw/problem_statuses contest_statuses/]});
+       my @problems = map { {problem => $_->get_column('problem'), solved => $_->solved} } $user->problem_statuses;
+       my @contests = map { {contest => $_->get_column('contest'), rank => $_->rank, score => $_->score} } $user->contest_statuses;
+       +{ $user->get_columns, problems => \@problems, contests => \@contests }
 }
 
 sub problem_list {
@@ -187,6 +190,29 @@ sub job_entry {
        \%params
 }
 
+sub update_status {
+       my ($self) = @_;
+       my @jobs = $self->jobs->search(undef, {cache => 1})->all;
+       my %hash;
+       $hash{$_->get_column('problem'), $_->get_column('owner')} = [$_, $_->result ? 1 : 0] for @jobs;
+       my @problem_statuses = map { [split ($;), @{$hash{$_}} ] } keys %hash;
+
+       my @contest_statuses = map {
+               my $contest = $_->id;
+               my @standings = $self->standings($contest);
+               map { [$contest, $_->{user}, $_->{score}, $_->{rank}] } @standings;
+       } $self->contests->all;
+
+       my $txn = sub {
+               $self->problem_statuses->delete;
+               $self->problem_statuses->populate([[qw/problem owner job solved/], @problem_statuses]);
+               $self->contest_statuses->delete;
+               $self->contest_statuses->populate([[qw/contest owner score rank/], @contest_statuses]);
+       };
+
+       $self->txn_do($txn);
+}
+
 1;
 
 __END__
index 166d5448d429b306eefc29c23aa8800179eb87ba..59334433c08e839b4c4aa5c3306b1cfc28d7abf5 100644 (file)
@@ -93,6 +93,21 @@ __PACKAGE__->has_many(
   { cascade_copy => 0, cascade_delete => 0 },
 );
 
+=head2 contest_statuses
+
+Type: has_many
+
+Related object: L<Gruntmaster::Data::Result::ContestStatus>
+
+=cut
+
+__PACKAGE__->has_many(
+  "contest_statuses",
+  "Gruntmaster::Data::Result::ContestStatus",
+  { "foreign.contest" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
 =head2 jobs
 
 Type: has_many
@@ -149,8 +164,8 @@ Composing rels: L</contest_problems> -> problem
 __PACKAGE__->many_to_many("problems", "contest_problems", "problem");
 
 
-# Created by DBIx::Class::Schema::Loader v0.07039 @ 2014-05-16 15:03:32
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:8PPzBpDmSTq4ukKuxIlLlQ
+# Created by DBIx::Class::Schema::Loader v0.07042 @ 2014-12-11 23:51:27
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:nu+Io9AhYkzYCky5UpCaKQ
 
 sub is_pending {
        my ($self, $time) = @_;
diff --git a/lib/Gruntmaster/Data/Result/ContestStatus.pm b/lib/Gruntmaster/Data/Result/ContestStatus.pm
new file mode 100644 (file)
index 0000000..dae7cbe
--- /dev/null
@@ -0,0 +1,113 @@
+use utf8;
+package Gruntmaster::Data::Result::ContestStatus;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+=head1 NAME
+
+Gruntmaster::Data::Result::ContestStatus
+
+=cut
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 TABLE: C<contest_status>
+
+=cut
+
+__PACKAGE__->table("contest_status");
+
+=head1 ACCESSORS
+
+=head2 contest
+
+  data_type: 'text'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 owner
+
+  data_type: 'text'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 score
+
+  data_type: 'integer'
+  is_nullable: 0
+
+=head2 rank
+
+  data_type: 'integer'
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "contest",
+  { data_type => "text", is_foreign_key => 1, is_nullable => 0 },
+  "owner",
+  { data_type => "text", is_foreign_key => 1, is_nullable => 0 },
+  "score",
+  { data_type => "integer", is_nullable => 0 },
+  "rank",
+  { data_type => "integer", is_nullable => 0 },
+);
+
+=head1 PRIMARY KEY
+
+=over 4
+
+=item * L</owner>
+
+=item * L</contest>
+
+=back
+
+=cut
+
+__PACKAGE__->set_primary_key("owner", "contest");
+
+=head1 RELATIONS
+
+=head2 contest
+
+Type: belongs_to
+
+Related object: L<Gruntmaster::Data::Result::Contest>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "contest",
+  "Gruntmaster::Data::Result::Contest",
+  { id => "contest" },
+  { is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
+);
+
+=head2 owner
+
+Type: belongs_to
+
+Related object: L<Gruntmaster::Data::Result::User>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "owner",
+  "Gruntmaster::Data::Result::User",
+  { id => "owner" },
+  { is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07042 @ 2014-12-11 23:51:27
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vfOfZeATPRODifpgHO4L0A
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
index 0bb76b9355cf76efb40536311c5fe0d82bf167de..66aed554fb875568f2eadd23e51539ddc7198351 100644 (file)
@@ -201,9 +201,24 @@ __PACKAGE__->belongs_to(
   { is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
 );
 
+=head2 problem_statuses
 
-# Created by DBIx::Class::Schema::Loader v0.07039 @ 2014-05-16 15:03:32
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:k3Oq7pNqFoCI5NwY5GaWfQ
+Type: has_many
+
+Related object: L<Gruntmaster::Data::Result::ProblemStatus>
+
+=cut
+
+__PACKAGE__->has_many(
+  "problem_statuses",
+  "Gruntmaster::Data::Result::ProblemStatus",
+  { "foreign.job" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07042 @ 2014-12-11 23:51:27
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:D49ekK0vGg/7b8xXZoYTWQ
 
 sub rerun {
        shift->update({daemon => undef, result => -2, result_text => undef});
index 098701eb10d4c2e3e981aa705b37f8630a2e2733..6359b99af1740672be8e88f8842f5a42facfee79 100644 (file)
@@ -251,6 +251,21 @@ __PACKAGE__->belongs_to(
   { is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
 );
 
+=head2 problem_statuses
+
+Type: has_many
+
+Related object: L<Gruntmaster::Data::Result::ProblemStatus>
+
+=cut
+
+__PACKAGE__->has_many(
+  "problem_statuses",
+  "Gruntmaster::Data::Result::ProblemStatus",
+  { "foreign.problem" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
 =head2 contests
 
 Type: many_to_many
@@ -262,8 +277,8 @@ Composing rels: L</contest_problems> -> contest
 __PACKAGE__->many_to_many("contests", "contest_problems", "contest");
 
 
-# Created by DBIx::Class::Schema::Loader v0.07042 @ 2014-12-07 00:51:56
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:KjAtOerTqBqtcMrBtwJ3Bw
+# Created by DBIx::Class::Schema::Loader v0.07042 @ 2014-12-11 23:51:27
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1SnNCeJdFr5lM3mmO6rtqA
 
 sub is_private {
        my ($self, $time) = @_;
diff --git a/lib/Gruntmaster/Data/Result/ProblemStatus.pm b/lib/Gruntmaster/Data/Result/ProblemStatus.pm
new file mode 100644 (file)
index 0000000..09920a7
--- /dev/null
@@ -0,0 +1,138 @@
+use utf8;
+package Gruntmaster::Data::Result::ProblemStatus;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+=head1 NAME
+
+Gruntmaster::Data::Result::ProblemStatus
+
+=cut
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 TABLE: C<problem_status>
+
+=cut
+
+__PACKAGE__->table("problem_status");
+
+=head1 ACCESSORS
+
+=head2 problem
+
+  data_type: 'text'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 owner
+
+  data_type: 'text'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 job
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_foreign_key: 1
+  is_nullable: 0
+  sequence: 'problem_status_job_seq'
+
+=head2 solved
+
+  data_type: 'boolean'
+  default_value: false
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "problem",
+  { data_type => "text", is_foreign_key => 1, is_nullable => 0 },
+  "owner",
+  { data_type => "text", is_foreign_key => 1, is_nullable => 0 },
+  "job",
+  {
+    data_type         => "integer",
+    is_auto_increment => 1,
+    is_foreign_key    => 1,
+    is_nullable       => 0,
+    sequence          => "problem_status_job_seq",
+  },
+  "solved",
+  { data_type => "boolean", default_value => \"false", is_nullable => 0 },
+);
+
+=head1 PRIMARY KEY
+
+=over 4
+
+=item * L</owner>
+
+=item * L</problem>
+
+=back
+
+=cut
+
+__PACKAGE__->set_primary_key("owner", "problem");
+
+=head1 RELATIONS
+
+=head2 job
+
+Type: belongs_to
+
+Related object: L<Gruntmaster::Data::Result::Job>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "job",
+  "Gruntmaster::Data::Result::Job",
+  { id => "job" },
+  { is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
+);
+
+=head2 owner
+
+Type: belongs_to
+
+Related object: L<Gruntmaster::Data::Result::User>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "owner",
+  "Gruntmaster::Data::Result::User",
+  { id => "owner" },
+  { is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
+);
+
+=head2 problem
+
+Type: belongs_to
+
+Related object: L<Gruntmaster::Data::Result::Problem>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "problem",
+  "Gruntmaster::Data::Result::Problem",
+  { id => "problem" },
+  { is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07042 @ 2014-12-11 23:51:27
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SUAwYQhgBtoCjtFSOMc4FQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
index 54e6b52c2527653ddaca9496655f4d8d01533547..c5547c1f776c3b179e54f292600ce7edf26289ad 100644 (file)
@@ -113,6 +113,21 @@ __PACKAGE__->set_primary_key("id");
 
 =head1 RELATIONS
 
+=head2 contest_statuses
+
+Type: has_many
+
+Related object: L<Gruntmaster::Data::Result::ContestStatus>
+
+=cut
+
+__PACKAGE__->has_many(
+  "contest_statuses",
+  "Gruntmaster::Data::Result::ContestStatus",
+  { "foreign.owner" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
 =head2 contests
 
 Type: has_many
@@ -158,6 +173,21 @@ __PACKAGE__->has_many(
   { cascade_copy => 0, cascade_delete => 0 },
 );
 
+=head2 problem_statuses
+
+Type: has_many
+
+Related object: L<Gruntmaster::Data::Result::ProblemStatus>
+
+=cut
+
+__PACKAGE__->has_many(
+  "problem_statuses",
+  "Gruntmaster::Data::Result::ProblemStatus",
+  { "foreign.owner" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
 =head2 problems
 
 Type: has_many
@@ -174,8 +204,8 @@ __PACKAGE__->has_many(
 );
 
 
-# Created by DBIx::Class::Schema::Loader v0.07039 @ 2014-05-16 15:23:08
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Cho4zmn58Mytf2jHvgP+4g
+# Created by DBIx::Class::Schema::Loader v0.07042 @ 2014-12-11 23:51:27
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JcVHC/n8J+NgJge9LkckYA
 
 use Authen::Passphrase;
 use Authen::Passphrase::BlowfishCrypt;
This page took 0.022137 seconds and 4 git commands to generate.