From 6d86389768264064ea165e307963ce487a0ddf6a Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Mon, 8 May 2017 20:54:50 +0100 Subject: [PATCH 01/12] Fix missed part from customer display name change --- lib/Pear/LocalLoop/Schema/Result/User.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Pear/LocalLoop/Schema/Result/User.pm b/lib/Pear/LocalLoop/Schema/Result/User.pm index 6974cb4..259a03b 100644 --- a/lib/Pear/LocalLoop/Schema/Result/User.pm +++ b/lib/Pear/LocalLoop/Schema/Result/User.pm @@ -138,7 +138,7 @@ sub name { my $self = shift; if ( defined $self->customer_id ) { - return $self->customer->name; + return $self->customer->display_name; } elsif ( defined $self->organisation_id ) { return $self->organisation->name; } else { From 22abc4e464e31f37851f905cb066328d7ad93d2e Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Mon, 15 May 2017 22:43:31 +0100 Subject: [PATCH 02/12] Fix postcode issue on upload --- lib/Pear/LocalLoop/Controller/Api/Upload.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Pear/LocalLoop/Controller/Api/Upload.pm b/lib/Pear/LocalLoop/Controller/Api/Upload.pm index c376129..5d2eb0c 100644 --- a/lib/Pear/LocalLoop/Controller/Api/Upload.pm +++ b/lib/Pear/LocalLoop/Controller/Api/Upload.pm @@ -70,6 +70,10 @@ has error_messages => sub { search_name => { required => { message => 'search_name is missing', status => 400 }, }, + postcode => { + required => { message => 'postcode is missing', status => 400 }, + postcode => { message => 'postcode must be valid', status => 400 }, + }, }; }; From 86955c0d1c6077128de893c22a2361acf303fe5e Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Tue, 16 May 2017 21:30:38 +0100 Subject: [PATCH 03/12] Started with basic today stats --- lib/Pear/LocalLoop.pm | 1 + lib/Pear/LocalLoop/Controller/Api/Stats.pm | 22 ++++++ .../LocalLoop/Schema/ResultSet/Transaction.pm | 29 ++++++++ lib/Test/Pear/LocalLoop.pm | 25 +++++++ t/api/stats.t | 73 +++++++++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 lib/Pear/LocalLoop/Controller/Api/Stats.pm create mode 100644 lib/Pear/LocalLoop/Schema/ResultSet/Transaction.pm create mode 100644 t/api/stats.t diff --git a/lib/Pear/LocalLoop.pm b/lib/Pear/LocalLoop.pm index aae42c2..d507c7b 100644 --- a/lib/Pear/LocalLoop.pm +++ b/lib/Pear/LocalLoop.pm @@ -147,6 +147,7 @@ sub startup { $api->post('/edit')->to('api-api#post_edit'); $api->post('/fetchuser')->to('api-api#post_fetchuser'); $api->post('/user-history')->to('api-user#post_user_history'); + $api->post('/stats')->to('api-stats#post_today'); my $api_admin = $api->under('/')->to('api-admin#auth'); diff --git a/lib/Pear/LocalLoop/Controller/Api/Stats.pm b/lib/Pear/LocalLoop/Controller/Api/Stats.pm new file mode 100644 index 0000000..45b1fc3 --- /dev/null +++ b/lib/Pear/LocalLoop/Controller/Api/Stats.pm @@ -0,0 +1,22 @@ +package Pear::LocalLoop::Controller::Api::Stats; +use Mojo::Base 'Mojolicious::Controller'; + +sub post_today { + my $c = shift; + + my $user = $c->stash->{api_user}; + + my $today_rs = $user->transactions->today_rs; + my $today_sum = $today_rs->get_column('value')->sum; + my $today_count = $today_rs->count; + + return $c->render( json => { + success => Mojo::JSON->true, + today_sum => $today_sum, + today_count => $today_count, + today_avg => $today_sum / $today_count, + }); + +} + +1; diff --git a/lib/Pear/LocalLoop/Schema/ResultSet/Transaction.pm b/lib/Pear/LocalLoop/Schema/ResultSet/Transaction.pm new file mode 100644 index 0000000..49d31a0 --- /dev/null +++ b/lib/Pear/LocalLoop/Schema/ResultSet/Transaction.pm @@ -0,0 +1,29 @@ +package Pear::LocalLoop::Schema::ResultSet::Transaction; + +use strict; +use warnings; + +use base 'DBIx::Class::ResultSet'; + +use DateTime; + +sub today_rs { + my ( $self ) = @_; + + my $dtf = $self->result_source->schema->storage->datetime_parser; + return $self->search({ + submitted_at => { + -between => [ + $dtf->format_datetime(DateTime->today()), + $dtf->format_datetime(DateTime->today()->add( days => 1 )), + ], + }, + }); +} + +sub today_for_user { + my ( $self, $user ) = @_; + return $self->search({ buyer_id => $user->id })->today_rs; +} + +1; diff --git a/lib/Test/Pear/LocalLoop.pm b/lib/Test/Pear/LocalLoop.pm index 606f109..7955370 100644 --- a/lib/Test/Pear/LocalLoop.pm +++ b/lib/Test/Pear/LocalLoop.pm @@ -65,6 +65,16 @@ sub register_customer { ->json_is('/success', Mojo::JSON->true)->or($self->dump_error); } +sub register_organisation { + my ( $self, $args ) = @_; + + $args->{usertype} = 'organisation'; + + $self->framework->post_ok('/api/register' => json => $args) + ->status_is(200)->or($self->dump_error) + ->json_is('/success', Mojo::JSON->true)->or($self->dump_error); +} + sub login { my $self = shift; my $args = shift; @@ -76,4 +86,19 @@ sub login { return $self->framework->tx->res->json->{session_key}; } +sub gen_upload { + my ( $self, $args ) = @_; + + my $file = { + content => '', + filename => 'text.jpg', + 'Content-Type' => 'image/jpeg', + }; + + return { + json => Mojo::JSON::encode_json($args), + file => $file, + }; +} + 1; diff --git a/t/api/stats.t b/t/api/stats.t new file mode 100644 index 0000000..d199b11 --- /dev/null +++ b/t/api/stats.t @@ -0,0 +1,73 @@ +use Mojo::Base -strict; + +use Test::More; +use Mojo::JSON; +use Test::Pear::LocalLoop; +use DateTime; + +my $framework = Test::Pear::LocalLoop->new; +my $t = $framework->framework; +my $schema = $t->app->schema; + +my $user = { + token => 'a', + full_name => 'Test User', + display_name => 'Test User', + email => 'test@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $org = { + token => 'b', + email => 'test2@example.com', + name => 'Test Org', + street_name => 'Test Street', + town => 'Lancaster', + postcode => 'LA1 1AA', + password => 'abc123', +}; + +$schema->resultset('AccountToken')->create({ name => $user->{token} }); +$schema->resultset('AccountToken')->create({ name => $org->{token} }); + +$framework->register_customer($user); +$framework->register_organisation($org); + +my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); +my $user_result = $schema->resultset('User')->find({ email => $user->{email} }); + +for ( 1 .. 10 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_, + proof_image => 'a', + }); +} + +my $dtf = $schema->storage->datetime_parser; +is $user_result->transactions->search({ + submitted_at => { + -between => [ + $dtf->format_datetime(DateTime->today()), + $dtf->format_datetime(DateTime->today()->add( days => 1 )), + ], + }, +})->get_column('value')->sum, 55, 'Got correct sum'; +is $user_result->transactions->today_rs->get_column('value')->sum, 55, 'Got correct sum through rs'; +is $schema->resultset('Transaction')->today_for_user($user_result)->get_column('value')->sum, 55, 'Got correct sum through rs'; + +my $session_key = $framework->login({ + email => $user->{email}, + password => $user->{password}, +}); + +$t->post_ok('/api/stats' => json => { session_key => $session_key } ) + ->status_is(200) + ->json_is('/success', Mojo::JSON->true) + ->json_is('/today_sum', 55) + ->json_is('/today_count', 10) + ->json_is('/today_avg', 5.5); + +done_testing; From a680d7b9df5620a161ae642ff242aac41ebd2559 Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Tue, 16 May 2017 21:46:44 +0100 Subject: [PATCH 04/12] Remove today_avg as redundant due to other data --- lib/Pear/LocalLoop/Controller/Api/Stats.pm | 1 - t/api/stats.t | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/Pear/LocalLoop/Controller/Api/Stats.pm b/lib/Pear/LocalLoop/Controller/Api/Stats.pm index 45b1fc3..af2931a 100644 --- a/lib/Pear/LocalLoop/Controller/Api/Stats.pm +++ b/lib/Pear/LocalLoop/Controller/Api/Stats.pm @@ -14,7 +14,6 @@ sub post_today { success => Mojo::JSON->true, today_sum => $today_sum, today_count => $today_count, - today_avg => $today_sum / $today_count, }); } diff --git a/t/api/stats.t b/t/api/stats.t index d199b11..5c06cee 100644 --- a/t/api/stats.t +++ b/t/api/stats.t @@ -67,7 +67,6 @@ $t->post_ok('/api/stats' => json => { session_key => $session_key } ) ->status_is(200) ->json_is('/success', Mojo::JSON->true) ->json_is('/today_sum', 55) - ->json_is('/today_count', 10) - ->json_is('/today_avg', 5.5); + ->json_is('/today_count', 10); done_testing; From 9586d1a637f3d3aad49020e3ea0d0e3834b8f062 Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Tue, 16 May 2017 22:45:49 +0100 Subject: [PATCH 05/12] Add more stats to default stat set --- lib/Pear/LocalLoop.pm | 2 +- lib/Pear/LocalLoop/Controller/Api/Stats.pm | 27 +++++++++- .../LocalLoop/Schema/ResultSet/Transaction.pm | 30 +++++++++--- t/api/stats.t | 49 +++++++++++++++++-- 4 files changed, 95 insertions(+), 13 deletions(-) diff --git a/lib/Pear/LocalLoop.pm b/lib/Pear/LocalLoop.pm index d507c7b..6c58554 100644 --- a/lib/Pear/LocalLoop.pm +++ b/lib/Pear/LocalLoop.pm @@ -147,7 +147,7 @@ sub startup { $api->post('/edit')->to('api-api#post_edit'); $api->post('/fetchuser')->to('api-api#post_fetchuser'); $api->post('/user-history')->to('api-user#post_user_history'); - $api->post('/stats')->to('api-stats#post_today'); + $api->post('/stats')->to('api-stats#post_index'); my $api_admin = $api->under('/')->to('api-admin#auth'); diff --git a/lib/Pear/LocalLoop/Controller/Api/Stats.pm b/lib/Pear/LocalLoop/Controller/Api/Stats.pm index af2931a..41f15f0 100644 --- a/lib/Pear/LocalLoop/Controller/Api/Stats.pm +++ b/lib/Pear/LocalLoop/Controller/Api/Stats.pm @@ -1,7 +1,7 @@ package Pear::LocalLoop::Controller::Api::Stats; use Mojo::Base 'Mojolicious::Controller'; -sub post_today { +sub post_index { my $c = shift; my $user = $c->stash->{api_user}; @@ -10,12 +10,35 @@ sub post_today { my $today_sum = $today_rs->get_column('value')->sum; my $today_count = $today_rs->count; + my $week_rs = $user->transactions->week_rs; + my $week_sum = $week_rs->get_column('value')->sum; + my $week_count = $week_rs->count; + + my $month_rs = $user->transactions->month_rs; + my $month_sum = $month_rs->get_column('value')->sum; + my $month_count = $month_rs->count; + + my $user_rs = $user->transactions; + my $user_sum = $user_rs->get_column('value')->sum; + my $user_count = $user_rs->count; + + my $global_rs = $c->schema->resultset('Transaction'); + my $global_sum = $global_rs->get_column('value')->sum; + my $global_count = $global_rs->count; + return $c->render( json => { success => Mojo::JSON->true, today_sum => $today_sum, today_count => $today_count, + week_sum => $week_sum, + week_count => $week_count, + month_sum => $month_sum, + month_count => $month_count, + user_sum => $user_sum, + user_count => $user_count, + global_sum => $global_sum, + global_count => $global_count, }); - } 1; diff --git a/lib/Pear/LocalLoop/Schema/ResultSet/Transaction.pm b/lib/Pear/LocalLoop/Schema/ResultSet/Transaction.pm index 49d31a0..4345cf4 100644 --- a/lib/Pear/LocalLoop/Schema/ResultSet/Transaction.pm +++ b/lib/Pear/LocalLoop/Schema/ResultSet/Transaction.pm @@ -7,23 +7,39 @@ use base 'DBIx::Class::ResultSet'; use DateTime; -sub today_rs { - my ( $self ) = @_; +sub search_between { + my ( $self, $from, $to ) = @_; my $dtf = $self->result_source->schema->storage->datetime_parser; return $self->search({ submitted_at => { -between => [ - $dtf->format_datetime(DateTime->today()), - $dtf->format_datetime(DateTime->today()->add( days => 1 )), + $dtf->format_datetime($from), + $dtf->format_datetime($to), ], }, }); } -sub today_for_user { - my ( $self, $user ) = @_; - return $self->search({ buyer_id => $user->id })->today_rs; +sub today_rs { + my ( $self ) = @_; + + my $today = DateTime->today(); + return $self->search_between( $today, $today->clone->add( days => 1 ) ); +} + +sub week_rs { + my ( $self ) = @_; + + my $today = DateTime->today(); + return $self->search_between( $today->clone->subtract( days => 7 ), $today ); +} + +sub month_rs { + my ( $self ) = @_; + + my $today = DateTime->today(); + return $self->search_between( $today->clone->subtract( days => 30 ), $today ); } 1; diff --git a/t/api/stats.t b/t/api/stats.t index 5c06cee..01d6d21 100644 --- a/t/api/stats.t +++ b/t/api/stats.t @@ -8,6 +8,7 @@ use DateTime; my $framework = Test::Pear::LocalLoop->new; my $t = $framework->framework; my $schema = $t->app->schema; +my $dtf = $schema->storage->datetime_parser; my $user = { token => 'a', @@ -46,7 +47,42 @@ for ( 1 .. 10 ) { }); } -my $dtf = $schema->storage->datetime_parser; +for ( 11 .. 20 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_, + proof_image => 'a', + submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 5 )), + }); +} + +for ( 21 .. 30 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_, + proof_image => 'a', + submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 25 )), + }); +} + +for ( 31 .. 40 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_, + proof_image => 'a', + submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 50 )), + }); +} + +for ( 41 .. 50 ) { + $org_result->user->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_, + proof_image => 'a', + submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 50 )), + }); +} + is $user_result->transactions->search({ submitted_at => { -between => [ @@ -56,7 +92,6 @@ is $user_result->transactions->search({ }, })->get_column('value')->sum, 55, 'Got correct sum'; is $user_result->transactions->today_rs->get_column('value')->sum, 55, 'Got correct sum through rs'; -is $schema->resultset('Transaction')->today_for_user($user_result)->get_column('value')->sum, 55, 'Got correct sum through rs'; my $session_key = $framework->login({ email => $user->{email}, @@ -67,6 +102,14 @@ $t->post_ok('/api/stats' => json => { session_key => $session_key } ) ->status_is(200) ->json_is('/success', Mojo::JSON->true) ->json_is('/today_sum', 55) - ->json_is('/today_count', 10); + ->json_is('/today_count', 10) + ->json_is('/week_sum', 155) + ->json_is('/week_count', 10) + ->json_is('/month_sum', 410) + ->json_is('/month_count', 20) + ->json_is('/user_sum', 820) + ->json_is('/user_count', 40) + ->json_is('/global_sum', 1275) + ->json_is('/global_count', 50); done_testing; From 00e98f874ac2cb6e3df5dd6059641b84e268f447 Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Mon, 22 May 2017 22:21:05 +0100 Subject: [PATCH 06/12] Get sums to show 0 instead of undef for no data --- lib/Pear/LocalLoop/Controller/Api/Stats.pm | 10 ++++----- t/api/stats.t | 24 +++++++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/Pear/LocalLoop/Controller/Api/Stats.pm b/lib/Pear/LocalLoop/Controller/Api/Stats.pm index 41f15f0..5e59953 100644 --- a/lib/Pear/LocalLoop/Controller/Api/Stats.pm +++ b/lib/Pear/LocalLoop/Controller/Api/Stats.pm @@ -28,15 +28,15 @@ sub post_index { return $c->render( json => { success => Mojo::JSON->true, - today_sum => $today_sum, + today_sum => $today_sum || 0, today_count => $today_count, - week_sum => $week_sum, + week_sum => $week_sum || 0, week_count => $week_count, - month_sum => $month_sum, + month_sum => $month_sum || 0, month_count => $month_count, - user_sum => $user_sum, + user_sum => $user_sum || 0, user_count => $user_count, - global_sum => $global_sum, + global_sum => $global_sum || 0, global_count => $global_count, }); } diff --git a/t/api/stats.t b/t/api/stats.t index 01d6d21..180a91f 100644 --- a/t/api/stats.t +++ b/t/api/stats.t @@ -39,6 +39,25 @@ $framework->register_organisation($org); my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); my $user_result = $schema->resultset('User')->find({ email => $user->{email} }); +my $session_key = $framework->login({ + email => $user->{email}, + password => $user->{password}, +}); + +$t->post_ok('/api/stats' => json => { session_key => $session_key } ) + ->status_is(200) + ->json_is('/success', Mojo::JSON->true) + ->json_is('/today_sum', 0) + ->json_is('/today_count', 0) + ->json_is('/week_sum', 0) + ->json_is('/week_count', 0) + ->json_is('/month_sum', 0) + ->json_is('/month_count', 0) + ->json_is('/user_sum', 0) + ->json_is('/user_count', 0) + ->json_is('/global_sum', 0) + ->json_is('/global_count', 0); + for ( 1 .. 10 ) { $user_result->create_related( 'transactions', { seller_id => $org_result->id, @@ -93,11 +112,6 @@ is $user_result->transactions->search({ })->get_column('value')->sum, 55, 'Got correct sum'; is $user_result->transactions->today_rs->get_column('value')->sum, 55, 'Got correct sum through rs'; -my $session_key = $framework->login({ - email => $user->{email}, - password => $user->{password}, -}); - $t->post_ok('/api/stats' => json => { session_key => $session_key } ) ->status_is(200) ->json_is('/success', Mojo::JSON->true) From bc12dbf8b01b85ee8ff14b2a778813e5d1282484 Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Tue, 23 May 2017 23:06:07 +0100 Subject: [PATCH 07/12] Added leaderboard first pass stuff --- .../LocalLoop/Schema/Result/Leaderboard.pm | 202 ++++++++++++++++++ .../LocalLoop/Schema/Result/LeaderboardSet.pm | 52 +++++ .../Schema/Result/LeaderboardValue.pm | 61 ++++++ lib/Test/Pear/LocalLoop.pm | 12 ++ script/deploy_db | 12 ++ t/schema/leaderboard.t | 178 +++++++++++++++ 6 files changed, 517 insertions(+) create mode 100644 lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm create mode 100644 lib/Pear/LocalLoop/Schema/Result/LeaderboardSet.pm create mode 100644 lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm create mode 100644 t/schema/leaderboard.t diff --git a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm new file mode 100644 index 0000000..80da852 --- /dev/null +++ b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm @@ -0,0 +1,202 @@ +package Pear::LocalLoop::Schema::Result::Leaderboard; + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +use DateTime; + +__PACKAGE__->table("leaderboards"); + +__PACKAGE__->add_columns( + "id" => { + data_type => "integer", + is_auto_increment => 1, + is_nullable => 0, + }, + "name" => { + data_type => "varchar", + size => 255, + is_nullable => 0, + }, + "type" => { + data_type => "varchar", + size => 255, + is_nullable => 0, + }, +); + +__PACKAGE__->set_primary_key("id"); + +__PACKAGE__->add_unique_constraint(["type"]); + +__PACKAGE__->has_many( + "sets", + "Pear::LocalLoop::Schema::Result::LeaderboardSet", + { "foreign.leaderboard_id" => "self.id" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + +sub create_new { + my $self = shift; + my $start = shift; + + my $type = $self->type; + + if ( $type eq 'daily_total' ) { + return $self->_create_total_set( $start, $start->clone->add( days => 1 ) ); + } elsif ( $type eq 'weekly_total' ) { + return $self->_create_total_set( $start, $start->clone->add( days => 7 ) ); + } elsif ( $type eq 'monthly_total' ) { + return $self->_create_total_set( $start, $start->clone->add( months => 1 ) ); + } elsif ( $type eq 'all_time_total' ) { + return $self->_create_total_all_time; + } elsif ( $type eq 'daily_count' ) { + return $self->_create_count_set( $start, $start->clone->add( days => 1 ) ); + } elsif ( $type eq 'weekly_count' ) { + return $self->_create_count_set( $start, $start->clone->add( days => 7 ) ); + } elsif ( $type eq 'monthly_count' ) { + return $self->_create_count_set( $start, $start->clone->add( months => 1 ) ); + } elsif ( $type eq 'all_time_count' ) { + return $self->_create_count_all_time; + } + warn "Unrecognised type"; + return $self; +} + +sub _create_total_set { + my ( $self, $start, $end ) = @_; + + my $schema = $self->result_source->schema; + + my $user_rs = $schema->resultset('User'); + + my @leaderboard; + + while ( my $user_result = $user_rs->next ) { + my $transaction_rs = $user_result->transactions->search_between( $start, $end ); + + my $transaction_sum = $transaction_rs->get_column('value')->sum; + + push @leaderboard, { + user_id => $user_result->id, + value => $transaction_sum || 0, + }; + } + + $self->create_related( + 'sets', + { + date => $start, + values => \@leaderboard, + }, + ); + + return $self; +} + +sub _create_count_set { + my ( $self, $start, $end ) = @_; + + my $schema = $self->result_source->schema; + + my $user_rs = $schema->resultset('User'); + + my @leaderboard; + + while ( my $user_result = $user_rs->next ) { + my $transaction_rs = $user_result->transactions->search_between( $start, $end ); + + my $transaction_count = $transaction_rs->count; + + push @leaderboard, { + user_id => $user_result->id, + value => $transaction_count || 0, + }; + } + + $self->create_related( + 'sets', + { + date => $start, + values => \@leaderboard, + }, + ); + + return $self; +} + +sub _create_total_all_time { + my ( $self ) = @_; + + my $schema = $self->result_source->schema; + + my $user_rs = $schema->resultset('User'); + + my $end = DateTime->today; + + my @leaderboard; + + while ( my $user_result = $user_rs->next ) { + my $transaction_rs = $user_result->transactions; + + my $transaction_sum = $transaction_rs->get_column('value')->sum; + + push @leaderboard, { + user_id => $user_result->id, + value => $transaction_sum || 0, + }; + } + + $self->create_related( + 'sets', + { + date => $end, + values => \@leaderboard, + }, + ); + + return $self; +} + +sub _create_count_all_time { + my ( $self ) = @_; + + my $schema = $self->result_source->schema; + + my $user_rs = $schema->resultset('User'); + + my $end = DateTime->today; + + my @leaderboard; + + while ( my $user_result = $user_rs->next ) { + my $transaction_rs = $user_result->transactions; + + my $transaction_count = $transaction_rs->count; + + push @leaderboard, { + user_id => $user_result->id, + value => $transaction_count || 0, + }; + } + + $self->create_related( + 'sets', + { + date => $end, + values => \@leaderboard, + }, + ); + + return $self; +} + +sub get_latest { + my $self = shift; + + return $self; +} + +1; diff --git a/lib/Pear/LocalLoop/Schema/Result/LeaderboardSet.pm b/lib/Pear/LocalLoop/Schema/Result/LeaderboardSet.pm new file mode 100644 index 0000000..f1a072f --- /dev/null +++ b/lib/Pear/LocalLoop/Schema/Result/LeaderboardSet.pm @@ -0,0 +1,52 @@ +package Pear::LocalLoop::Schema::Result::LeaderboardSet; + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +__PACKAGE__->load_components( qw/ + InflateColumn::DateTime +/); + +__PACKAGE__->table("leaderboard_sets"); + +__PACKAGE__->add_columns( + "id" => { + data_type => "integer", + is_auto_increment => 1, + is_nullable => 0, + }, + "leaderboard_id" => { + data_type => "integer", + is_foreign_key => 1, + is_nullable => 0, + }, + "date" => { + data_type => "datetime", + is_nullable => 0, + }, +); + +__PACKAGE__->set_primary_key("id"); + +__PACKAGE__->belongs_to( + "leaderboard", + "Pear::LocalLoop::Schema::Result::Leaderboard", + { "foreign.id" => "self.leaderboard_id" }, + { + is_deferrable => 0, + join_type => "LEFT", + on_delete => "NO ACTION", + on_update => "NO ACTION", + }, +); + +__PACKAGE__->has_many( + "values", + "Pear::LocalLoop::Schema::Result::LeaderboardValue", + { "foreign.set_id" => "self.id" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + +1; diff --git a/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm b/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm new file mode 100644 index 0000000..29aa59e --- /dev/null +++ b/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm @@ -0,0 +1,61 @@ +package Pear::LocalLoop::Schema::Result::LeaderboardValue; + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +__PACKAGE__->table("leaderboard_values"); + +__PACKAGE__->add_columns( + "id" => { + data_type => "integer", + is_auto_increment => 1, + is_nullable => 0, + }, + "user_id" => { + data_type => "integer", + is_foreign_key => 1, + is_nullable => 0, + }, + "set_id" => { + data_type => "integer", + is_foreign_key => 1, + is_nullable => 0, + }, + "value" => { + data_type => "decimal", + size => [ 16, 2 ], + is_nullable => 0, + }, +); + +__PACKAGE__->set_primary_key("id"); + +__PACKAGE__->add_unique_constraint([qw/ user_id set_id /]); + +__PACKAGE__->belongs_to( + "set", + "Pear::LocalLoop::Schema::Result::LeaderboardSet", + { "foreign.id" => "self.set_id" }, + { + is_deferrable => 0, + join_type => "LEFT", + on_delete => "NO ACTION", + on_update => "NO ACTION", + }, +); + +__PACKAGE__->belongs_to( + "user", + "Pear::LocalLoop::Schema::Result::User", + { "foreign.id" => "self.user_id" }, + { + is_deferrable => 0, + join_type => "LEFT", + on_delete => "NO ACTION", + on_update => "NO ACTION", + }, +); + +1; diff --git a/lib/Test/Pear/LocalLoop.pm b/lib/Test/Pear/LocalLoop.pm index 7955370..9581b02 100644 --- a/lib/Test/Pear/LocalLoop.pm +++ b/lib/Test/Pear/LocalLoop.pm @@ -37,6 +37,18 @@ has framework => sub { [ '50+' ], ]); + $schema->resultset('Leaderboard')->populate([ + [ qw/ name type / ], + [ 'Daily Total', 'daily_total' ], + [ 'Daily Count', 'daily_count' ], + [ 'Weekly Total', 'weekly_total' ], + [ 'Weekly Count', 'weekly_count' ], + [ 'Monthly Total', 'monthly_total' ], + [ 'Monthly Count', 'monthly_count' ], + [ 'All Time Total', 'all_time_total' ], + [ 'All Time Count', 'all_time_count' ], + ]); + return $t; }; diff --git a/script/deploy_db b/script/deploy_db index 9354a6d..d0e4c19 100755 --- a/script/deploy_db +++ b/script/deploy_db @@ -21,6 +21,18 @@ $schema->resultset('AgeRange')->populate([ [ '50+' ], ]); +$schema->resultset('Leaderboard')->populate([ + [ qw/ name type / ], + [ 'Daily Total', 'daily_total' ], + [ 'Daily Count', 'daily_count' ], + [ 'Weekly Total', 'weekly_total' ], + [ 'Weekly Count', 'weekly_count' ], + [ 'Monthly Total', 'monthly_total' ], + [ 'Monthly Count', 'monthly_count' ], + [ 'All Time Total', 'all_time_total' ], + [ 'All Time Count', 'all_time_count' ], +]); + if (defined $ENV{MOJO_MODE} && $ENV{MOJO_MODE} eq 'development' ) { $schema->resultset('User')->create({ diff --git a/t/schema/leaderboard.t b/t/schema/leaderboard.t new file mode 100644 index 0000000..2cf96f8 --- /dev/null +++ b/t/schema/leaderboard.t @@ -0,0 +1,178 @@ +use Mojo::Base -strict; + +use Test::More; +use Mojo::JSON; +use Test::Pear::LocalLoop; +use DateTime; + +my $framework = Test::Pear::LocalLoop->new; +my $t = $framework->framework; +my $schema = $t->app->schema; +my $dtf = $schema->storage->datetime_parser; + +my $user1 = { + token => 'a', + full_name => 'Test User1', + display_name => 'Test User1', + email => 'test1@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user2 = { + token => 'b', + full_name => 'Test User2', + display_name => 'Test User2', + email => 'test2@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user3 = { + token => 'c', + full_name => 'Test User3', + display_name => 'Test User3', + email => 'test3@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user4 = { + token => 'd', + full_name => 'Test User4', + display_name => 'Test User4', + email => 'test4@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $org = { + token => 'e', + email => 'test5@example.com', + name => 'Test Org', + street_name => 'Test Street', + town => 'Lancaster', + postcode => 'LA1 1AA', + password => 'abc123', +}; + +$schema->resultset('AccountToken')->create({ name => $_->{token} }) + for ( $user1, $user2, $user3, $user4, $org ); + +$framework->register_customer($_) + for ( $user1, $user2, $user3, $user4 ); + +$framework->register_organisation($org); + +my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); + +my $tweak = 0; + +for my $user ( $user1, $user2, $user3, $user4 ) { + $tweak ++; + my $user_result = $schema->resultset('User')->find({ email => $user->{email} }); + for ( 1 .. 10 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_ + $tweak, + proof_image => 'a', + }); + } + + for ( 11 .. 20 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_ + $tweak, + proof_image => 'a', + submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 5 )), + }); + } + + for ( 21 .. 30 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_ + $tweak, + proof_image => 'a', + submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 25 )), + }); + } + + for ( 31 .. 40 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_ + $tweak, + proof_image => 'a', + submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 50 )), + }); + } + + is $user_result->transactions->count, 40, 'correct count for user' . $tweak; +} + +sub test_leaderboard { + my ( $title, $name, $date, $expected ) = @_; + + subtest $title => sub { + my $leaderboard_rs = $schema->resultset('Leaderboard'); + + my $today_board = $leaderboard_rs->find({ type => $name })->create_new($date)->sets->first; + + is $today_board->values->count, 5, 'correct value count for today'; + + my $today_values = $today_board->values->search( + {}, + { + order_by => { -desc => 'value' }, + columns => [ qw/ user_id value / ], + }, + ); + $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); + + is_deeply [ $today_values->all ], $expected, 'Today as expected'; + }; +} + +test_leaderboard( + 'Daily Total', + 'daily_total', + DateTime->today, + [ + { user_id => 4, value => 95 }, + { user_id => 3, value => 85 }, + { user_id => 2, value => 75 }, + { user_id => 1, value => 65 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Daily Count', + 'daily_count', + DateTime->today, + [ + { user_id => 1, value => 10 }, + { user_id => 2, value => 10 }, + { user_id => 3, value => 10 }, + { user_id => 4, value => 10 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Weekly Total', + 'weekly_total', + DateTime->today->subtract( days => 7 ), + [ + { user_id => 4, value => 195 }, + { user_id => 3, value => 185 }, + { user_id => 2, value => 175 }, + { user_id => 1, value => 165 }, + { user_id => 5, value => 0 }, + ] +); + +done_testing; From 2e3bc489ea93c605b612bfa230666a532cb4fcf6 Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Tue, 23 May 2017 23:16:56 +0100 Subject: [PATCH 08/12] Finish tests for leaderboard schema output --- t/schema/leaderboard.t | 69 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/t/schema/leaderboard.t b/t/schema/leaderboard.t index 2cf96f8..6dd3e6c 100644 --- a/t/schema/leaderboard.t +++ b/t/schema/leaderboard.t @@ -121,7 +121,7 @@ sub test_leaderboard { my $today_board = $leaderboard_rs->find({ type => $name })->create_new($date)->sets->first; - is $today_board->values->count, 5, 'correct value count for today'; + is $today_board->values->count, 5, 'correct value count'; my $today_values = $today_board->values->search( {}, @@ -132,7 +132,7 @@ sub test_leaderboard { ); $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); - is_deeply [ $today_values->all ], $expected, 'Today as expected'; + is_deeply [ $today_values->all ], $expected, 'array as expected'; }; } @@ -175,4 +175,69 @@ test_leaderboard( ] ); +test_leaderboard( + 'Weekly Count', + 'weekly_count', + DateTime->today->subtract( days => 7 ), + [ + { user_id => 1, value => 10 }, + { user_id => 2, value => 10 }, + { user_id => 3, value => 10 }, + { user_id => 4, value => 10 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Monthly Total', + 'monthly_total', + DateTime->today->subtract( months => 1 ), + [ + { user_id => 4, value => 490 }, + { user_id => 3, value => 470 }, + { user_id => 2, value => 450 }, + { user_id => 1, value => 430 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Monthly Count', + 'monthly_count', + DateTime->today->subtract( months => 1 ), + [ + { user_id => 1, value => 20 }, + { user_id => 2, value => 20 }, + { user_id => 3, value => 20 }, + { user_id => 4, value => 20 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'All Time Total', + 'all_time_total', + DateTime->today, + [ + { user_id => 4, value => 980 }, + { user_id => 3, value => 940 }, + { user_id => 2, value => 900 }, + { user_id => 1, value => 860 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'All Time Count', + 'all_time_count', + DateTime->today, + [ + { user_id => 1, value => 40 }, + { user_id => 2, value => 40 }, + { user_id => 3, value => 40 }, + { user_id => 4, value => 40 }, + { user_id => 5, value => 0 }, + ] +); + done_testing; From 7e11fc50bdf1b753c8c9d31912f0959ed93ca376 Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Mon, 5 Jun 2017 22:15:27 +0100 Subject: [PATCH 09/12] Added get_latest and test for it on leaderboard --- .../LocalLoop/Schema/Result/Leaderboard.pm | 6 +- t/schema/leaderboard.t | 56 +++++++++++++++---- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm index 80da852..6b70998 100644 --- a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm +++ b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm @@ -196,7 +196,11 @@ sub _create_count_all_time { sub get_latest { my $self = shift; - return $self; + my $latest = $self->search_related('sets', {}, { + order_by => { -desc => 'date' }, + })->first; + + return $latest; } 1; diff --git a/t/schema/leaderboard.t b/t/schema/leaderboard.t index 6dd3e6c..13e8a82 100644 --- a/t/schema/leaderboard.t +++ b/t/schema/leaderboard.t @@ -72,6 +72,8 @@ my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} my $tweak = 0; +my $now = DateTime->today(); + for my $user ( $user1, $user2, $user3, $user4 ) { $tweak ++; my $user_result = $schema->resultset('User')->find({ email => $user->{email} }); @@ -88,7 +90,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', - submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 5 )), + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 5 )), }); } @@ -97,7 +99,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', - submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 25 )), + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 25 )), }); } @@ -106,7 +108,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', - submitted_at => $dtf->format_datetime(DateTime->today()->subtract( days => 50 )), + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 50 )), }); } @@ -119,7 +121,7 @@ sub test_leaderboard { subtest $title => sub { my $leaderboard_rs = $schema->resultset('Leaderboard'); - my $today_board = $leaderboard_rs->find({ type => $name })->create_new($date)->sets->first; + my $today_board = $leaderboard_rs->find({ type => $name })->create_new($date)->get_latest; is $today_board->values->count, 5, 'correct value count'; @@ -139,7 +141,7 @@ sub test_leaderboard { test_leaderboard( 'Daily Total', 'daily_total', - DateTime->today, + $now, [ { user_id => 4, value => 95 }, { user_id => 3, value => 85 }, @@ -152,7 +154,7 @@ test_leaderboard( test_leaderboard( 'Daily Count', 'daily_count', - DateTime->today, + $now, [ { user_id => 1, value => 10 }, { user_id => 2, value => 10 }, @@ -165,7 +167,7 @@ test_leaderboard( test_leaderboard( 'Weekly Total', 'weekly_total', - DateTime->today->subtract( days => 7 ), + $now->clone->subtract( days => 7 ), [ { user_id => 4, value => 195 }, { user_id => 3, value => 185 }, @@ -178,7 +180,7 @@ test_leaderboard( test_leaderboard( 'Weekly Count', 'weekly_count', - DateTime->today->subtract( days => 7 ), + $now->clone->subtract( days => 7 ), [ { user_id => 1, value => 10 }, { user_id => 2, value => 10 }, @@ -191,7 +193,7 @@ test_leaderboard( test_leaderboard( 'Monthly Total', 'monthly_total', - DateTime->today->subtract( months => 1 ), + $now->clone->subtract( months => 1 ), [ { user_id => 4, value => 490 }, { user_id => 3, value => 470 }, @@ -204,7 +206,7 @@ test_leaderboard( test_leaderboard( 'Monthly Count', 'monthly_count', - DateTime->today->subtract( months => 1 ), + $now->clone->subtract( months => 1 ), [ { user_id => 1, value => 20 }, { user_id => 2, value => 20 }, @@ -217,7 +219,7 @@ test_leaderboard( test_leaderboard( 'All Time Total', 'all_time_total', - DateTime->today, + $now, [ { user_id => 4, value => 980 }, { user_id => 3, value => 940 }, @@ -230,7 +232,7 @@ test_leaderboard( test_leaderboard( 'All Time Count', 'all_time_count', - DateTime->today, + $now, [ { user_id => 1, value => 40 }, { user_id => 2, value => 40 }, @@ -240,4 +242,34 @@ test_leaderboard( ] ); +subtest 'get_latest' => sub { + my $leaderboard_rs = $schema->resultset('Leaderboard'); + $leaderboard_rs->find({ type => 'daily_total' })->create_new($now->clone->subtract(days => 5)); + $leaderboard_rs->find({ type => 'daily_total' })->create_new($now->clone->subtract(days => 25)); + $leaderboard_rs->find({ type => 'daily_total' })->create_new($now->clone->subtract(days => 50)); + + my $today_board = $leaderboard_rs->find({ type => 'daily_total' })->get_latest; + + is $today_board->values->count, 5, 'correct value count'; + + my $today_values = $today_board->values->search( + {}, + { + order_by => { -desc => 'value' }, + columns => [ qw/ user_id value / ], + }, + ); + $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); + + my $expected = [ + { user_id => 4, value => 95 }, + { user_id => 3, value => 85 }, + { user_id => 2, value => 75 }, + { user_id => 1, value => 65 }, + { user_id => 5, value => 0 }, + ]; + + is_deeply [ $today_values->all ], $expected, 'array as expected'; +}; + done_testing; From 5e06f0a69bbb02ab699df25e5bc1469108decb82 Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Mon, 5 Jun 2017 22:34:48 +0100 Subject: [PATCH 10/12] Added resultset for leaderboard for more consice code --- .../LocalLoop/Schema/ResultSet/Leaderboard.pm | 42 +++ t/schema/leaderboard.t | 2 + t/schema/resultset_leaderboard.t | 279 ++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 lib/Pear/LocalLoop/Schema/ResultSet/Leaderboard.pm create mode 100644 t/schema/resultset_leaderboard.t diff --git a/lib/Pear/LocalLoop/Schema/ResultSet/Leaderboard.pm b/lib/Pear/LocalLoop/Schema/ResultSet/Leaderboard.pm new file mode 100644 index 0000000..d8fd9c6 --- /dev/null +++ b/lib/Pear/LocalLoop/Schema/ResultSet/Leaderboard.pm @@ -0,0 +1,42 @@ +package Pear::LocalLoop::Schema::ResultSet::Leaderboard; + +use strict; +use warnings; + +use base 'DBIx::Class::ResultSet'; + +sub get_latest { + my $self = shift; + my $type = shift; + + my $type_result = $self->find_by_type( $type ); + + return undef unless defined $type_result; + + my $latest = $type_result->search_related('sets', {}, { + order_by => { -desc => 'date' }, + })->first; + + return $latest; +} + +sub create_new { + my $self = shift; + my $type = shift; + my $date = shift; + + my $type_result = $self->find_by_type($type); + + return undef unless $type_result; + + return $type_result->create_new($date); +} + +sub find_by_type { + my $self = shift; + my $type = shift; + + return $self->find({ type => $type }); +} + +1; diff --git a/t/schema/leaderboard.t b/t/schema/leaderboard.t index 13e8a82..e0c3fb8 100644 --- a/t/schema/leaderboard.t +++ b/t/schema/leaderboard.t @@ -270,6 +270,8 @@ subtest 'get_latest' => sub { ]; is_deeply [ $today_values->all ], $expected, 'array as expected'; + + is $leaderboard_rs->find({ type => 'daily_total' })->sets->count, 4, 'correct leaderboard count'; }; done_testing; diff --git a/t/schema/resultset_leaderboard.t b/t/schema/resultset_leaderboard.t new file mode 100644 index 0000000..f14912f --- /dev/null +++ b/t/schema/resultset_leaderboard.t @@ -0,0 +1,279 @@ +use Mojo::Base -strict; + +use Test::More; +use Mojo::JSON; +use Test::Pear::LocalLoop; +use DateTime; + +my $framework = Test::Pear::LocalLoop->new; +my $t = $framework->framework; +my $schema = $t->app->schema; +my $dtf = $schema->storage->datetime_parser; + +my $user1 = { + token => 'a', + full_name => 'Test User1', + display_name => 'Test User1', + email => 'test1@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user2 = { + token => 'b', + full_name => 'Test User2', + display_name => 'Test User2', + email => 'test2@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user3 = { + token => 'c', + full_name => 'Test User3', + display_name => 'Test User3', + email => 'test3@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user4 = { + token => 'd', + full_name => 'Test User4', + display_name => 'Test User4', + email => 'test4@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $org = { + token => 'e', + email => 'test5@example.com', + name => 'Test Org', + street_name => 'Test Street', + town => 'Lancaster', + postcode => 'LA1 1AA', + password => 'abc123', +}; + +$schema->resultset('AccountToken')->create({ name => $_->{token} }) + for ( $user1, $user2, $user3, $user4, $org ); + +$framework->register_customer($_) + for ( $user1, $user2, $user3, $user4 ); + +$framework->register_organisation($org); + +my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); + +my $tweak = 0; + +my $now = DateTime->today(); + +for my $user ( $user1, $user2, $user3, $user4 ) { + $tweak ++; + my $user_result = $schema->resultset('User')->find({ email => $user->{email} }); + for ( 1 .. 10 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_ + $tweak, + proof_image => 'a', + }); + } + + for ( 11 .. 20 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_ + $tweak, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 5 )), + }); + } + + for ( 21 .. 30 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_ + $tweak, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 25 )), + }); + } + + for ( 31 .. 40 ) { + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => $_ + $tweak, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 50 )), + }); + } + + is $user_result->transactions->count, 40, 'correct count for user' . $tweak; +} + +sub test_leaderboard { + my ( $title, $name, $date, $expected ) = @_; + + subtest $title => sub { + my $leaderboard_rs = $schema->resultset('Leaderboard'); + + $leaderboard_rs->create_new( $name, $date ); + + my $today_board = $leaderboard_rs->get_latest( $name ); + + is $today_board->values->count, 5, 'correct value count'; + + my $today_values = $today_board->values->search( + {}, + { + order_by => { -desc => 'value' }, + columns => [ qw/ user_id value / ], + }, + ); + $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); + + is_deeply [ $today_values->all ], $expected, 'array as expected'; + }; +} + +test_leaderboard( + 'Daily Total', + 'daily_total', + $now, + [ + { user_id => 4, value => 95 }, + { user_id => 3, value => 85 }, + { user_id => 2, value => 75 }, + { user_id => 1, value => 65 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Daily Count', + 'daily_count', + $now, + [ + { user_id => 1, value => 10 }, + { user_id => 2, value => 10 }, + { user_id => 3, value => 10 }, + { user_id => 4, value => 10 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Weekly Total', + 'weekly_total', + $now->clone->subtract( days => 7 ), + [ + { user_id => 4, value => 195 }, + { user_id => 3, value => 185 }, + { user_id => 2, value => 175 }, + { user_id => 1, value => 165 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Weekly Count', + 'weekly_count', + $now->clone->subtract( days => 7 ), + [ + { user_id => 1, value => 10 }, + { user_id => 2, value => 10 }, + { user_id => 3, value => 10 }, + { user_id => 4, value => 10 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Monthly Total', + 'monthly_total', + $now->clone->subtract( months => 1 ), + [ + { user_id => 4, value => 490 }, + { user_id => 3, value => 470 }, + { user_id => 2, value => 450 }, + { user_id => 1, value => 430 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'Monthly Count', + 'monthly_count', + $now->clone->subtract( months => 1 ), + [ + { user_id => 1, value => 20 }, + { user_id => 2, value => 20 }, + { user_id => 3, value => 20 }, + { user_id => 4, value => 20 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'All Time Total', + 'all_time_total', + $now, + [ + { user_id => 4, value => 980 }, + { user_id => 3, value => 940 }, + { user_id => 2, value => 900 }, + { user_id => 1, value => 860 }, + { user_id => 5, value => 0 }, + ] +); + +test_leaderboard( + 'All Time Count', + 'all_time_count', + $now, + [ + { user_id => 1, value => 40 }, + { user_id => 2, value => 40 }, + { user_id => 3, value => 40 }, + { user_id => 4, value => 40 }, + { user_id => 5, value => 0 }, + ] +); + +subtest 'get_latest' => sub { + my $leaderboard_rs = $schema->resultset('Leaderboard'); + $leaderboard_rs->create_new( 'daily_total', $now->clone->subtract(days => 5)); + $leaderboard_rs->create_new( 'daily_total', $now->clone->subtract(days => 25)); + $leaderboard_rs->create_new( 'daily_total', $now->clone->subtract(days => 50)); + + my $today_board = $leaderboard_rs->get_latest( 'daily_total' ); + + is $today_board->values->count, 5, 'correct value count'; + + my $today_values = $today_board->values->search( + {}, + { + order_by => { -desc => 'value' }, + columns => [ qw/ user_id value / ], + }, + ); + $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); + + my $expected = [ + { user_id => 4, value => 95 }, + { user_id => 3, value => 85 }, + { user_id => 2, value => 75 }, + { user_id => 1, value => 65 }, + { user_id => 5, value => 0 }, + ]; + + is_deeply [ $today_values->all ], $expected, 'array as expected'; + + is $leaderboard_rs->find_by_type( 'daily_total' )->sets->count, 4, 'correct leaderboard count'; +}; + +done_testing; From daeeac811c17b2524e35e6e68b58844a0bd906b2 Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Mon, 5 Jun 2017 22:59:31 +0100 Subject: [PATCH 11/12] Change leaderboards to only follow customers --- .../LocalLoop/Schema/Result/Leaderboard.pm | 37 ++++++++----------- t/schema/leaderboard.t | 13 +------ t/schema/resultset_leaderboard.t | 13 +------ 3 files changed, 20 insertions(+), 43 deletions(-) diff --git a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm index 6b70998..4b5bc21 100644 --- a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm +++ b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm @@ -51,7 +51,7 @@ sub create_new { } elsif ( $type eq 'monthly_total' ) { return $self->_create_total_set( $start, $start->clone->add( months => 1 ) ); } elsif ( $type eq 'all_time_total' ) { - return $self->_create_total_all_time; + return $self->_create_total_all_time( $start ); } elsif ( $type eq 'daily_count' ) { return $self->_create_count_set( $start, $start->clone->add( days => 1 ) ); } elsif ( $type eq 'weekly_count' ) { @@ -59,18 +59,23 @@ sub create_new { } elsif ( $type eq 'monthly_count' ) { return $self->_create_count_set( $start, $start->clone->add( months => 1 ) ); } elsif ( $type eq 'all_time_count' ) { - return $self->_create_count_all_time; + return $self->_create_count_all_time( $start ); } warn "Unrecognised type"; return $self; } +sub _get_customer_rs { + my $self = shift; + return $self->result_source->schema->resultset('User')->search({ + organisation_id => undef, + }); +} + sub _create_total_set { my ( $self, $start, $end ) = @_; - my $schema = $self->result_source->schema; - - my $user_rs = $schema->resultset('User'); + my $user_rs = $self->_get_customer_rs; my @leaderboard; @@ -99,9 +104,7 @@ sub _create_total_set { sub _create_count_set { my ( $self, $start, $end ) = @_; - my $schema = $self->result_source->schema; - - my $user_rs = $schema->resultset('User'); + my $user_rs = $self->_get_customer_rs; my @leaderboard; @@ -128,14 +131,10 @@ sub _create_count_set { } sub _create_total_all_time { - my ( $self ) = @_; - - my $schema = $self->result_source->schema; - - my $user_rs = $schema->resultset('User'); - - my $end = DateTime->today; + my ( $self, $end ) = @_; + my $user_rs = $self->_get_customer_rs; + my @leaderboard; while ( my $user_result = $user_rs->next ) { @@ -161,13 +160,9 @@ sub _create_total_all_time { } sub _create_count_all_time { - my ( $self ) = @_; + my ( $self, $end ) = @_; - my $schema = $self->result_source->schema; - - my $user_rs = $schema->resultset('User'); - - my $end = DateTime->today; + my $user_rs = $self->_get_customer_rs; my @leaderboard; diff --git a/t/schema/leaderboard.t b/t/schema/leaderboard.t index e0c3fb8..b03106a 100644 --- a/t/schema/leaderboard.t +++ b/t/schema/leaderboard.t @@ -123,7 +123,7 @@ sub test_leaderboard { my $today_board = $leaderboard_rs->find({ type => $name })->create_new($date)->get_latest; - is $today_board->values->count, 5, 'correct value count'; + is $today_board->values->count, 4, 'correct value count'; my $today_values = $today_board->values->search( {}, @@ -147,7 +147,6 @@ test_leaderboard( { user_id => 3, value => 85 }, { user_id => 2, value => 75 }, { user_id => 1, value => 65 }, - { user_id => 5, value => 0 }, ] ); @@ -160,7 +159,6 @@ test_leaderboard( { user_id => 2, value => 10 }, { user_id => 3, value => 10 }, { user_id => 4, value => 10 }, - { user_id => 5, value => 0 }, ] ); @@ -173,7 +171,6 @@ test_leaderboard( { user_id => 3, value => 185 }, { user_id => 2, value => 175 }, { user_id => 1, value => 165 }, - { user_id => 5, value => 0 }, ] ); @@ -186,7 +183,6 @@ test_leaderboard( { user_id => 2, value => 10 }, { user_id => 3, value => 10 }, { user_id => 4, value => 10 }, - { user_id => 5, value => 0 }, ] ); @@ -199,7 +195,6 @@ test_leaderboard( { user_id => 3, value => 470 }, { user_id => 2, value => 450 }, { user_id => 1, value => 430 }, - { user_id => 5, value => 0 }, ] ); @@ -212,7 +207,6 @@ test_leaderboard( { user_id => 2, value => 20 }, { user_id => 3, value => 20 }, { user_id => 4, value => 20 }, - { user_id => 5, value => 0 }, ] ); @@ -225,7 +219,6 @@ test_leaderboard( { user_id => 3, value => 940 }, { user_id => 2, value => 900 }, { user_id => 1, value => 860 }, - { user_id => 5, value => 0 }, ] ); @@ -238,7 +231,6 @@ test_leaderboard( { user_id => 2, value => 40 }, { user_id => 3, value => 40 }, { user_id => 4, value => 40 }, - { user_id => 5, value => 0 }, ] ); @@ -250,7 +242,7 @@ subtest 'get_latest' => sub { my $today_board = $leaderboard_rs->find({ type => 'daily_total' })->get_latest; - is $today_board->values->count, 5, 'correct value count'; + is $today_board->values->count, 4, 'correct value count'; my $today_values = $today_board->values->search( {}, @@ -266,7 +258,6 @@ subtest 'get_latest' => sub { { user_id => 3, value => 85 }, { user_id => 2, value => 75 }, { user_id => 1, value => 65 }, - { user_id => 5, value => 0 }, ]; is_deeply [ $today_values->all ], $expected, 'array as expected'; diff --git a/t/schema/resultset_leaderboard.t b/t/schema/resultset_leaderboard.t index f14912f..76d622e 100644 --- a/t/schema/resultset_leaderboard.t +++ b/t/schema/resultset_leaderboard.t @@ -125,7 +125,7 @@ sub test_leaderboard { my $today_board = $leaderboard_rs->get_latest( $name ); - is $today_board->values->count, 5, 'correct value count'; + is $today_board->values->count, 4, 'correct value count'; my $today_values = $today_board->values->search( {}, @@ -149,7 +149,6 @@ test_leaderboard( { user_id => 3, value => 85 }, { user_id => 2, value => 75 }, { user_id => 1, value => 65 }, - { user_id => 5, value => 0 }, ] ); @@ -162,7 +161,6 @@ test_leaderboard( { user_id => 2, value => 10 }, { user_id => 3, value => 10 }, { user_id => 4, value => 10 }, - { user_id => 5, value => 0 }, ] ); @@ -175,7 +173,6 @@ test_leaderboard( { user_id => 3, value => 185 }, { user_id => 2, value => 175 }, { user_id => 1, value => 165 }, - { user_id => 5, value => 0 }, ] ); @@ -188,7 +185,6 @@ test_leaderboard( { user_id => 2, value => 10 }, { user_id => 3, value => 10 }, { user_id => 4, value => 10 }, - { user_id => 5, value => 0 }, ] ); @@ -201,7 +197,6 @@ test_leaderboard( { user_id => 3, value => 470 }, { user_id => 2, value => 450 }, { user_id => 1, value => 430 }, - { user_id => 5, value => 0 }, ] ); @@ -214,7 +209,6 @@ test_leaderboard( { user_id => 2, value => 20 }, { user_id => 3, value => 20 }, { user_id => 4, value => 20 }, - { user_id => 5, value => 0 }, ] ); @@ -227,7 +221,6 @@ test_leaderboard( { user_id => 3, value => 940 }, { user_id => 2, value => 900 }, { user_id => 1, value => 860 }, - { user_id => 5, value => 0 }, ] ); @@ -240,7 +233,6 @@ test_leaderboard( { user_id => 2, value => 40 }, { user_id => 3, value => 40 }, { user_id => 4, value => 40 }, - { user_id => 5, value => 0 }, ] ); @@ -252,7 +244,7 @@ subtest 'get_latest' => sub { my $today_board = $leaderboard_rs->get_latest( 'daily_total' ); - is $today_board->values->count, 5, 'correct value count'; + is $today_board->values->count, 4, 'correct value count'; my $today_values = $today_board->values->search( {}, @@ -268,7 +260,6 @@ subtest 'get_latest' => sub { { user_id => 3, value => 85 }, { user_id => 2, value => 75 }, { user_id => 1, value => 65 }, - { user_id => 5, value => 0 }, ]; is_deeply [ $today_values->all ], $expected, 'array as expected'; From 9cd9f8079edc9028e0fce70a73670a6076bc617f Mon Sep 17 00:00:00 2001 From: Tom Bloor Date: Tue, 6 Jun 2017 22:30:02 +0100 Subject: [PATCH 12/12] Add leaderboard stats endpoint and trends --- lib/Pear/LocalLoop.pm | 1 + lib/Pear/LocalLoop/Controller/Api/Stats.pm | 56 +++++ .../LocalLoop/Schema/Result/Leaderboard.pm | 99 +++++++++ .../Schema/Result/LeaderboardValue.pm | 4 + t/api/stats_leaderboards.t | 198 +++++++++++++++++ t/schema/leaderboard_trend.t | 199 ++++++++++++++++++ 6 files changed, 557 insertions(+) create mode 100644 t/api/stats_leaderboards.t create mode 100644 t/schema/leaderboard_trend.t diff --git a/lib/Pear/LocalLoop.pm b/lib/Pear/LocalLoop.pm index 6c58554..540f1a2 100644 --- a/lib/Pear/LocalLoop.pm +++ b/lib/Pear/LocalLoop.pm @@ -148,6 +148,7 @@ sub startup { $api->post('/fetchuser')->to('api-api#post_fetchuser'); $api->post('/user-history')->to('api-user#post_user_history'); $api->post('/stats')->to('api-stats#post_index'); + $api->post('/stats/leaderboard')->to('api-stats#post_leaderboards'); my $api_admin = $api->under('/')->to('api-admin#auth'); diff --git a/lib/Pear/LocalLoop/Controller/Api/Stats.pm b/lib/Pear/LocalLoop/Controller/Api/Stats.pm index 5e59953..357562b 100644 --- a/lib/Pear/LocalLoop/Controller/Api/Stats.pm +++ b/lib/Pear/LocalLoop/Controller/Api/Stats.pm @@ -1,6 +1,17 @@ package Pear::LocalLoop::Controller::Api::Stats; use Mojo::Base 'Mojolicious::Controller'; +use List::Util qw/ first /; + +has error_messages => sub { + return { + type => { + required => { message => 'Type of Leaderboard Required', status => 400 }, + in_resultset => { message => 'Unrecognised Leaderboard Type', status => 400 }, + }, + }; +}; + sub post_index { my $c = shift; @@ -41,4 +52,49 @@ sub post_index { }); } +sub post_leaderboards { + my $c = shift; + + my $validation = $c->validation; + $validation->input( $c->stash->{api_json} ); + + my $leaderboard_rs = $c->schema->resultset('Leaderboard'); + + $validation->required('type')->in_resultset( 'type', $leaderboard_rs ); + + return $c->api_validation_error if $validation->has_error; + + my $today_board = $leaderboard_rs->get_latest( $validation->param('type') ); + + my $today_values = $today_board->values->search( + {}, + { + order_by => { -desc => 'me.value' }, + columns => [ + qw/ + me.value + me.trend + me.user_id + /, + { display_name => 'customer.display_name' }, + ], + join => { user => 'customer' }, + }, + ); + $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); + + my @leaderboard_array = $today_values->all; + + my $current_user_index = first { $leaderboard_array[$_]->{user_id} == $c->stash->{api_user}->id } 0..$#leaderboard_array; + + # Dont leak user id's + map { delete $_->{user_id} } @leaderboard_array; + + return $c->render( json => { + success => Mojo::JSON->true, + leaderboard => [ @leaderboard_array ], + user_position => $current_user_index, + }); +} + 1; diff --git a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm index 4b5bc21..1105806 100644 --- a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm +++ b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm @@ -79,14 +79,39 @@ sub _create_total_set { my @leaderboard; + my $previous_board = $self->get_latest; + + if ( defined $previous_board ) { + $previous_board = $previous_board->values; + } + while ( my $user_result = $user_rs->next ) { my $transaction_rs = $user_result->transactions->search_between( $start, $end ); my $transaction_sum = $transaction_rs->get_column('value')->sum; + my $previous_value; + + if ( defined $previous_board ) { + $previous_value = $previous_board->find({ user_id => $user_result->id }); + } + + my $trend; + + if ( ! defined $previous_value ) { + $trend = 0; + } elsif ( $previous_value->value > $transaction_sum ) { + $trend = -1; + } elsif ( $previous_value->value < $transaction_sum ) { + $trend = 1; + } else { + $trend = 0; + } + push @leaderboard, { user_id => $user_result->id, value => $transaction_sum || 0, + trend => $trend, }; } @@ -108,14 +133,39 @@ sub _create_count_set { my @leaderboard; + my $previous_board = $self->get_latest; + + if ( defined $previous_board ) { + $previous_board = $previous_board->values; + } + while ( my $user_result = $user_rs->next ) { my $transaction_rs = $user_result->transactions->search_between( $start, $end ); my $transaction_count = $transaction_rs->count; + my $previous_value; + + if ( defined $previous_board ) { + $previous_value = $previous_board->find({ user_id => $user_result->id }); + } + + my $trend; + + if ( ! defined $previous_value ) { + $trend = 0; + } elsif ( $previous_value->value > $transaction_count ) { + $trend = -1; + } elsif ( $previous_value->value < $transaction_count ) { + $trend = 1; + } else { + $trend = 0; + } + push @leaderboard, { user_id => $user_result->id, value => $transaction_count || 0, + trend => $trend, }; } @@ -137,11 +187,35 @@ sub _create_total_all_time { my @leaderboard; + my $previous_board = $self->get_latest; + + if ( defined $previous_board ) { + $previous_board = $previous_board->values; + } + while ( my $user_result = $user_rs->next ) { my $transaction_rs = $user_result->transactions; my $transaction_sum = $transaction_rs->get_column('value')->sum; + my $previous_value; + + if ( defined $previous_board ) { + $previous_value = $previous_board->find({ user_id => $user_result->id }); + } + + my $trend; + + if ( ! defined $previous_value ) { + $trend = 0; + } elsif ( $previous_value->value > $transaction_sum ) { + $trend = -1; + } elsif ( $previous_value->value < $transaction_sum ) { + $trend = 1; + } else { + $trend = 0; + } + push @leaderboard, { user_id => $user_result->id, value => $transaction_sum || 0, @@ -166,14 +240,39 @@ sub _create_count_all_time { my @leaderboard; + my $previous_board = $self->get_latest; + + if ( defined $previous_board ) { + $previous_board = $previous_board->values; + } + while ( my $user_result = $user_rs->next ) { my $transaction_rs = $user_result->transactions; my $transaction_count = $transaction_rs->count; + my $previous_value; + + if ( defined $previous_board ) { + $previous_value = $previous_board->find({ user_id => $user_result->id }); + } + + my $trend; + + if ( ! defined $previous_value ) { + $trend = 0; + } elsif ( $previous_value->value > $transaction_count ) { + $trend = -1; + } elsif ( $previous_value->value < $transaction_count ) { + $trend = 1; + } else { + $trend = 0; + } + push @leaderboard, { user_id => $user_result->id, value => $transaction_count || 0, + trend => $trend, }; } diff --git a/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm b/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm index 29aa59e..4beaed4 100644 --- a/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm +++ b/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm @@ -28,6 +28,10 @@ __PACKAGE__->add_columns( size => [ 16, 2 ], is_nullable => 0, }, + "trend" => { + data_type => "integer", + default_value => 0, + }, ); __PACKAGE__->set_primary_key("id"); diff --git a/t/api/stats_leaderboards.t b/t/api/stats_leaderboards.t new file mode 100644 index 0000000..e8bf855 --- /dev/null +++ b/t/api/stats_leaderboards.t @@ -0,0 +1,198 @@ +use Mojo::Base -strict; + +use Test::More; +use Mojo::JSON; +use Test::Pear::LocalLoop; +use DateTime; + +my $framework = Test::Pear::LocalLoop->new; +my $t = $framework->framework; +my $schema = $t->app->schema; +my $dtf = $schema->storage->datetime_parser; + +my $user1 = { + token => 'a', + full_name => 'Test User1', + display_name => 'Test User1', + email => 'test1@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user2 = { + token => 'b', + full_name => 'Test User2', + display_name => 'Test User2', + email => 'test2@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user3 = { + token => 'c', + full_name => 'Test User3', + display_name => 'Test User3', + email => 'test3@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user4 = { + token => 'd', + full_name => 'Test User4', + display_name => 'Test User4', + email => 'test4@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $org = { + token => 'e', + email => 'test5@example.com', + name => 'Test Org', + street_name => 'Test Street', + town => 'Lancaster', + postcode => 'LA1 1AA', + password => 'abc123', +}; + +$schema->resultset('AccountToken')->create({ name => $_->{token} }) + for ( $user1, $user2, $user3, $user4, $org ); + +$framework->register_customer($_) + for ( $user1, $user2, $user3, $user4 ); + +$framework->register_organisation($org); + +my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); + +my $tweak = 0; + +my $now = DateTime->today(); + +{ + my $user_result = $schema->resultset('User')->find({ email => $user1->{email} }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 1, + proof_image => 'a', + }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 9, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 1 )), + }); +} + +{ + my $user_result = $schema->resultset('User')->find({ email => $user2->{email} }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 3, + proof_image => 'a', + }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 1, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 1 )), + }); +} + +{ + my $user_result = $schema->resultset('User')->find({ email => $user3->{email} }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 5, + proof_image => 'a', + }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 5, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 1 )), + }); +} + +{ + my $user_result = $schema->resultset('User')->find({ email => $user4->{email} }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 9, + proof_image => 'a', + }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 3, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 1 )), + }); +} + +my $session_key = $framework->login({ + email => $user1->{email}, + password => $user1->{password}, +}); + +sub test_leaderboard { + my ( $title, $name, $date, $expected, $user_place ) = @_; + + subtest $title => sub { + my $leaderboard_rs = $schema->resultset('Leaderboard'); + + $leaderboard_rs->create_new( $name, $date ); + + $t->post_ok('/api/stats/leaderboard' => json => { session_key => $session_key, type => $name } ) + ->status_is(200) + ->or($framework->dump_error) + ->json_is('/success', Mojo::JSON->true) + ->or($framework->dump_error) + ->json_is('/leaderboard', $expected) + ->or($framework->dump_error) + ->json_is('/user_position', $user_place) + ->or($framework->dump_error); + }; +} + +$schema->resultset('Leaderboard')->create_new( 'daily_total', $now->clone->subtract( days => 1 ) ); + +test_leaderboard( + 'Daily Total', + 'daily_total', + $now, + [ + { display_name => 'Test User4', value => 9, trend => 1 }, + { display_name => 'Test User3', value => 5, trend => 0 }, + { display_name => 'Test User2', value => 3, trend => 1 }, + { display_name => 'Test User1', value => 1, trend => -1}, + ], + 3 +); + +test_leaderboard( + 'Daily Count', + 'daily_count', + $now, + [ + { display_name => 'Test User1', value => 1, trend => 0 }, + { display_name => 'Test User2', value => 1, trend => 0 }, + { display_name => 'Test User3', value => 1, trend => 0 }, + { display_name => 'Test User4', value => 1, trend => 0 }, + ], + 0 +); + +done_testing; diff --git a/t/schema/leaderboard_trend.t b/t/schema/leaderboard_trend.t new file mode 100644 index 0000000..f8b370a --- /dev/null +++ b/t/schema/leaderboard_trend.t @@ -0,0 +1,199 @@ +use Mojo::Base -strict; + +use Test::More; +use Mojo::JSON; +use Test::Pear::LocalLoop; +use DateTime; + +my $framework = Test::Pear::LocalLoop->new; +my $t = $framework->framework; +my $schema = $t->app->schema; +my $dtf = $schema->storage->datetime_parser; + +my $user1 = { + token => 'a', + full_name => 'Test User1', + display_name => 'Test User1', + email => 'test1@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user2 = { + token => 'b', + full_name => 'Test User2', + display_name => 'Test User2', + email => 'test2@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user3 = { + token => 'c', + full_name => 'Test User3', + display_name => 'Test User3', + email => 'test3@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $user4 = { + token => 'd', + full_name => 'Test User4', + display_name => 'Test User4', + email => 'test4@example.com', + postcode => 'LA1 1AA', + password => 'abc123', + age_range => 1, +}; + +my $org = { + token => 'e', + email => 'test5@example.com', + name => 'Test Org', + street_name => 'Test Street', + town => 'Lancaster', + postcode => 'LA1 1AA', + password => 'abc123', +}; + +$schema->resultset('AccountToken')->create({ name => $_->{token} }) + for ( $user1, $user2, $user3, $user4, $org ); + +$framework->register_customer($_) + for ( $user1, $user2, $user3, $user4 ); + +$framework->register_organisation($org); + +my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); + +my $tweak = 0; + +my $now = DateTime->today(); + +{ + my $user_result = $schema->resultset('User')->find({ email => $user1->{email} }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 1, + proof_image => 'a', + }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 9, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 1 )), + }); +} + +{ + my $user_result = $schema->resultset('User')->find({ email => $user2->{email} }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 3, + proof_image => 'a', + }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 1, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 1 )), + }); +} + +{ + my $user_result = $schema->resultset('User')->find({ email => $user3->{email} }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 5, + proof_image => 'a', + }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 5, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 1 )), + }); +} + +{ + my $user_result = $schema->resultset('User')->find({ email => $user4->{email} }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 9, + proof_image => 'a', + }); + + $user_result->create_related( 'transactions', { + seller_id => $org_result->id, + value => 3, + proof_image => 'a', + submitted_at => $dtf->format_datetime($now->clone->subtract( days => 1 )), + }); +} + +my $session_key = $framework->login({ + email => $user1->{email}, + password => $user1->{password}, +}); + +sub test_leaderboard { + my ( $title, $name, $date, $expected ) = @_; + + subtest $title => sub { + my $leaderboard_rs = $schema->resultset('Leaderboard'); + + my $today_board = $leaderboard_rs->find({ type => $name })->create_new($date)->get_latest; + + is $today_board->values->count, 4, 'correct value count'; + + my $today_values = $today_board->values->search( + {}, + { + order_by => { -desc => 'value' }, + columns => [ qw/ user_id value trend / ], + }, + ); + $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); + + is_deeply [ $today_values->all ], $expected, 'array as expected'; + }; +} + +$schema->resultset('Leaderboard')->find({ type => 'daily_total' })->create_new( $now->clone->subtract( days => 1 ) ); + +test_leaderboard( + 'Daily Total', + 'daily_total', + $now, + [ + { user_id => 4, value => 9, trend => 1 }, + { user_id => 3, value => 5, trend => 0 }, + { user_id => 2, value => 3, trend => 1 }, + { user_id => 1, value => 1, trend => -1}, + ] +); + +test_leaderboard( + 'Daily Count', + 'daily_count', + $now, + [ + { user_id => 1, value => 1, trend => 0 }, + { user_id => 2, value => 1, trend => 0 }, + { user_id => 3, value => 1, trend => 0 }, + { user_id => 4, value => 1, trend => 0 }, + ] +); + +done_testing;