Merge pull request #84 from Pear-Trading/finn/customerdashboard

revamped API for customer dashboard
This commit is contained in:
Finn 2017-12-15 18:10:48 +00:00 committed by GitHub
commit 2dceb4067e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 737 additions and 139 deletions

View file

@ -6,6 +6,12 @@
a new tab a new tab
* **Admin Feature** Ability to add ESTA to entity Added * **Admin Feature** Ability to add ESTA to entity Added
* Trail map code updated * Trail map code updated
* Added API for customer graphs
* Revamped graphs code
* Added API for customer local purchase pie charts
* Added API for customer snippets
* Added API for sector purchase list for customer dashboard
* **Admin Feature** Fixed org sector on user edit layout and text
# v0.9.7 # v0.9.7

View file

@ -171,6 +171,12 @@ sub startup {
$api_v1_org->post('/employee')->to('api-organisation#post_employee_read'); $api_v1_org->post('/employee')->to('api-organisation#post_employee_read');
$api_v1_org->post('/employee/add')->to('api-organisation#post_employee_add'); $api_v1_org->post('/employee/add')->to('api-organisation#post_employee_add');
my $api_v1_cust = $api_v1->under('/customer')->to('api-v1-customer#auth');
$api_v1_cust->post('/graphs')->to('api-v1-customer-graphs#index');
$api_v1_cust->post('/snippets')->to('api-v1-customer-snippets#index');
$api_v1_cust->post('/pies')->to('api-v1-customer-pies#index');
my $admin_routes = $r->under('/admin')->to('admin#under'); my $admin_routes = $r->under('/admin')->to('admin#under');
$admin_routes->get('/home')->to('admin#home'); $admin_routes->get('/home')->to('admin#home');

View file

@ -92,6 +92,11 @@ sub update {
return $c->redirect_to( '/admin/users/' . $id ); return $c->redirect_to( '/admin/users/' . $id );
} }
my $location = $c->get_location_from_postcode(
$validation->param('postcode'),
$user->type,
);
if ( $user->type eq 'customer' ){ if ( $user->type eq 'customer' ){
try { try {
@ -100,6 +105,7 @@ sub update {
full_name => $validation->param('full_name'), full_name => $validation->param('full_name'),
display_name => $validation->param('display_name'), display_name => $validation->param('display_name'),
postcode => $validation->param('postcode'), postcode => $validation->param('postcode'),
( defined $location ? ( %$location ) : ( latitude => undef, longitude => undef ) ),
}); });
$user->update({ $user->update({
email => $validation->param('email'), email => $validation->param('email'),
@ -125,6 +131,7 @@ sub update {
town => $validation->param('town'), town => $validation->param('town'),
sector => $validation->param('sector'), sector => $validation->param('sector'),
postcode => $validation->param('postcode'), postcode => $validation->param('postcode'),
( defined $location ? ( %$location ) : ( latitude => undef, longitude => undef ) ),
}); });
$user->update({ $user->update({
email => $validation->param('email'), email => $validation->param('email'),

View file

@ -15,46 +15,64 @@ has error_messages => sub {
sub post_index { sub post_index {
my $c = shift; my $c = shift;
my $user = $c->stash->{api_user}->entity; my $entity = $c->stash->{api_user}->entity;
my $today_rs = $user->purchases->today_rs; my $duration = DateTime::Duration->new( weeks => 7 );
my $today_sum = $today_rs->get_column('value')->sum || 0; my $end = DateTime->today;
my $today_count = $today_rs->count; my $start = $end->clone->subtract_duration( $duration );
my $week_rs = $user->purchases->week_rs; my $weeks = { purchases => [] };
my $week_sum = $week_rs->get_column('value')->sum || 0; my $sectors = { sectors => [], purchases => [] };
my $week_count = $week_rs->count;
my $month_rs = $user->purchases->month_rs; my $dtf = $c->schema->storage->datetime_parser;
my $month_sum = $month_rs->get_column('value')->sum || 0; my $driver = $c->schema->storage->dbh->{Driver}->{Name};
my $month_count = $month_rs->count; my $week_transaction_rs = $c->schema->resultset('ViewQuantisedTransaction' . $driver)->search(
{
purchase_time => {
-between => [
$dtf->format_datetime($start),
$dtf->format_datetime($end),
],
},
buyer_id => $entity->id,
},
{
columns => [
{
quantised => 'quantised_weeks',
count => \"COUNT(*)",
}
],
group_by => 'quantised_weeks',
order_by => { '-asc' => 'quantised_weeks' },
}
);
my $user_rs = $user->purchases; for ( $week_transaction_rs->all ) {
my $user_sum = $user_rs->get_column('value')->sum || 0; push @{ $weeks->{ purchases } }, ($_->get_column('count') || 0);
my $user_count = $user_rs->count; }
my $global_rs = $c->schema->resultset('Transaction'); my $sector_purchase_rs = $entity->purchases->search({},
my $global_sum = $global_rs->get_column('value')->sum || 0; {
my $global_count = $global_rs->count; join => { 'seller' => 'organisation' },
columns => {
sector => "organisation.sector",
count => \"COUNT(*)",
},
group_by => "organisation.sector",
order_by => { '-desc' => "COUNT(*)" },
}
);
my $leaderboard_rs = $c->schema->resultset('Leaderboard'); for ( $sector_purchase_rs->all ) {
my $monthly_board = $leaderboard_rs->get_latest( 'monthly_total' ); push @{ $sectors->{ sectors } }, $_->get_column('sector');
my $monthly_values = $monthly_board->values; push @{ $sectors->{ purchases } }, ($_->get_column('count') || 0);
my $current_user_position = $monthly_values ? $monthly_values->find({ entity_id => $user->id }) : undef; }
return $c->render( json => { return $c->render( json => {
success => Mojo::JSON->true, success => Mojo::JSON->true,
today_sum => $today_sum / 100000, weeks => $weeks,
today_count => $today_count, sectors => $sectors,
week_sum => $week_sum / 100000,
week_count => $week_count,
month_sum => $month_sum / 100000,
month_count => $month_count,
user_sum => $user_sum / 100000,
user_count => $user_count,
global_sum => $global_sum / 100000,
global_count => $global_count,
user_position => defined $current_user_position ? $current_user_position->position : 0,
}); });
} }

View file

@ -146,12 +146,18 @@ sub post_upload {
return $c->api_validation_error if $validation->has_error; return $c->api_validation_error if $validation->has_error;
my $location = $c->get_location_from_postcode(
$validation->param('postcode'),
'organisation',
);
my $entity = $c->schema->resultset('Entity')->create_org({ my $entity = $c->schema->resultset('Entity')->create_org({
submitted_by_id => $user->id, submitted_by_id => $user->id,
name => $validation->param('organisation_name'), name => $validation->param('organisation_name'),
street_name => $validation->param('street_name'), street_name => $validation->param('street_name'),
town => $validation->param('town'), town => $validation->param('town'),
postcode => $validation->param('postcode'), postcode => $validation->param('postcode'),
( defined $location ? ( %$location ) : ( latitude => undef, longitude => undef ) ),
pending => 1, pending => 1,
}); });
$organisation = $entity->organisation; $organisation = $entity->organisation;

View file

@ -0,0 +1,21 @@
package Pear::LocalLoop::Controller::Api::V1::Customer;
use Mojo::Base 'Mojolicious::Controller';
sub auth {
my $c = shift;
return 1 if $c->stash->{api_user}->type eq 'customer';
$c->render(
json => {
success => Mojo::JSON->false,
message => 'Not an Customer',
error => 'user_not_cust',
},
status => 403,
);
return 0;
}
1;

View file

@ -0,0 +1,167 @@
package Pear::LocalLoop::Controller::Api::V1::Customer::Graphs;
use Mojo::Base 'Mojolicious::Controller';
has error_messages => sub {
return {
graph => {
required => { message => 'Must request graph type', status => 400 },
in => { message => 'Unrecognised graph type', status => 400 },
},
};
};
sub index {
my $c = shift;
my $validation = $c->validation;
$validation->input( $c->stash->{api_json} );
$validation->required('graph')->in( qw/
total_last_week
avg_spend_last_week
total_last_month
avg_spend_last_month
/ );
return $c->api_validation_error if $validation->has_error;
my $graph_sub = "graph_" . $validation->param('graph');
unless ( $c->can($graph_sub) ) {
# Secondary catch in case a mistake has been made
return $c->render(
json => {
success => Mojo::JSON->false,
message => $c->error_messages->{graph}->{in}->{message},
error => 'in',
},
status => $c->error_messages->{graph}->{in}->{status},
);
}
return $c->$graph_sub;
}
sub graph_total_last_week { return shift->_purchases_total_duration( 7 ) }
sub graph_total_last_month { return shift->_purchases_total_duration( 30 ) }
sub _purchases_total_duration {
my ( $c, $day_duration ) = @_;
my $duration = DateTime::Duration->new( days => $day_duration );
my $entity = $c->stash->{api_user}->entity;
my $data = { labels => [], data => [] };
my ( $start, $end ) = $c->_get_start_end_duration( $duration );
$data->{bounds} = {
min => $c->format_iso_datetime( $start ),
max => $c->format_iso_datetime( $end ),
};
while ( $start < $end ) {
my $next_end = $start->clone->add( days => 1 );
my $transactions = $entity->purchases
->search_between( $start, $next_end )
->get_column('value')
->sum || 0 * 1;
push @{ $data->{ labels } }, $c->format_iso_datetime( $start );
push @{ $data->{ data } }, $transactions / 100000;
$start->add( days => 1 );
}
return $c->render(
json => {
success => Mojo::JSON->true,
graph => $data,
}
);
}
sub graph_avg_spend_last_week { return shift->_purchases_avg_spend_duration( 7 ) }
sub graph_avg_spend_last_month { return shift->_purchases_avg_spend_duration( 30 ) }
sub _purchases_avg_spend_duration {
my ( $c, $day_duration ) = @_;
my $duration = DateTime::Duration->new( days => $day_duration );
my $entity = $c->stash->{api_user}->entity;
my $data = { labels => [], data => [] };
my ( $start, $end ) = $c->_get_start_end_duration( $duration );
$data->{bounds} = {
min => $c->format_iso_datetime( $start ),
max => $c->format_iso_datetime( $end ),
};
my $dtf = $c->schema->storage->datetime_parser;
my $driver = $c->schema->storage->dbh->{Driver}->{Name};
my $transaction_rs = $c->schema->resultset('ViewQuantisedTransaction' . $driver)->search(
{
purchase_time => {
-between => [
$dtf->format_datetime($start),
$dtf->format_datetime($end),
],
},
buyer_id => $entity->id,
},
{
columns => [
{
quantised => 'quantised_days',
count => \"COUNT(*)",
sum_value => $c->pg_or_sqlite(
'SUM("me"."value")',
'SUM("me"."value")',
),
average_value => $c->pg_or_sqlite(
'AVG("me"."value")',
'AVG("me"."value")',
),
}
],
group_by => 'quantised_days',
order_by => { '-asc' => 'quantised_days' },
}
);
for ( $transaction_rs->all ) {
my $quantised = $c->db_datetime_parser->parse_datetime($_->get_column('quantised'));
push @{ $data->{ labels } }, $c->format_iso_datetime( $quantised );
push @{ $data->{ data } }, ($_->get_column('average_value') || 0) / 100000;
}
return $c->render(
json => {
success => Mojo::JSON->true,
graph => $data,
}
);
}
sub _get_start_end_duration {
my ( $c, $duration ) = @_;
my $end = DateTime->today;
my $start = $end->clone->subtract_duration( $duration );
return ( $start, $end );
}
sub pg_or_sqlite {
my ( $c, $pg_sql, $sqlite_sql ) = @_;
my $driver = $c->schema->storage->dbh->{Driver}->{Name};
if ( $driver eq 'Pg' ) {
return \$pg_sql;
} elsif ( $driver eq 'SQLite' ) {
return \$sqlite_sql;
} else {
$c->app->log->warn('Unknown Driver Used');
return undef;
}
}
1;

View file

@ -0,0 +1,62 @@
package Pear::LocalLoop::Controller::Api::V1::Customer::Pies;
use Mojo::Base 'Mojolicious::Controller';
sub index {
my $c = shift;
my $entity = $c->stash->{api_user}->entity;
my $purchase_rs = $entity->purchases;
my $local_org_local_purchase = $purchase_rs->search({
"me.distance" => { '<', 20000 },
'organisation.is_local' => 1,
},
{
join => { 'seller' => 'organisation' },
}
);
my $local_org_non_local_purchase = $purchase_rs->search({
"me.distance" => { '>=', 20000 },
'organisation.is_local' => 1,
},
{
join => { 'seller' => 'organisation' },
}
);
my $non_local_org_local_purchase = $purchase_rs->search({
"me.distance" => { '<', 20000 },
'organisation.is_local' => 0,
},
{
join => { 'seller' => 'organisation' },
}
);
my $non_local_org_non_local_purchase = $purchase_rs->search({
"me.distance" => { '>=', 20000 },
'organisation.is_local' => 0,
},
{
join => { 'seller' => 'organisation' },
}
);
my $data = {
'Local shop local purchaser' => $local_org_local_purchase->count,
'Local shop non-local purchaser' => $local_org_non_local_purchase->count,
'Non-local shop local purchaser' => $non_local_org_local_purchase->count,
'Non-local shop non-local purchaser' => $non_local_org_non_local_purchase->count,
};
return $c->render(
json => {
success => Mojo::JSON->true,
pie => $data,
}
);
}
1;

View file

@ -0,0 +1,32 @@
package Pear::LocalLoop::Controller::Api::V1::Customer::Snippets;
use Mojo::Base 'Mojolicious::Controller';
sub index {
my $c = shift;
my $entity = $c->stash->{api_user}->entity;
my $data = {
user_sum => 0,
user_position => 0,
};
my $user_rs = $entity->purchases;
$data->{ user_sum } = $user_rs->get_column('value')->sum || 0;
$data->{ user_sum } /= 100000;
my $leaderboard_rs = $c->schema->resultset('Leaderboard');
my $monthly_board = $leaderboard_rs->get_latest( 'monthly_total' );
if (defined $monthly_board) {
my $monthly_values = $monthly_board->values;
$data->{ user_position } = $monthly_values ? $monthly_values->find({ entity_id => $entity->id })->position : 0;
}
return $c->render(
json => {
success => Mojo::JSON->true,
snippets => $data,
}
);
}
1;

View file

@ -78,35 +78,30 @@ sub graph_customers_range {
); );
} }
sub graph_customers_last_7_days { sub graph_customers_last_7_days { return shift->_customers_last_duration( 7 ) }
my $c = shift; sub graph_customers_last_30_days { return shift->_customers_last_duration( 30 ) }
my $duration = DateTime::Duration->new( days => 7 );
return $c->_customers_last_duration( $duration );
}
sub graph_customers_last_30_days {
my $c = shift;
my $duration = DateTime::Duration->new( days => 30 );
return $c->_customers_last_duration( $duration );
}
sub _customers_last_duration { sub _customers_last_duration {
my ( $c, $duration ) = @_; my ( $c, $day_duration ) = @_;
my $duration = DateTime::Duration->new( days => $day_duration );
my $entity = $c->stash->{api_user}->entity; my $entity = $c->stash->{api_user}->entity;
my $data = { labels => [], data => [] }; my $data = { labels => [], data => [] };
my ( $start, $end ) = $c->_get_start_end_duration( $duration ); my ( $start, $end ) = $c->_get_start_end_duration( $duration );
$data->{bounds} = {
min => $c->format_iso_datetime( $start ),
max => $c->format_iso_datetime( $end ),
};
while ( $start < $end ) { while ( $start < $end ) {
my $next_end = $start->clone->add( days => 1 ); my $next_end = $start->clone->add( days => 1 );
my $transactions = $entity->sales my $transactions = $entity->sales
->search_between( $start, $next_end ) ->search_between( $start, $next_end )
->count; ->count;
push @{ $data->{ labels } }, $start->day_name; push @{ $data->{ labels } }, $c->format_iso_datetime( $start );
push @{ $data->{ data } }, $transactions; push @{ $data->{ data } }, $transactions;
$start->add( days => 1 ); $start->add( days => 1 );
} }
@ -132,13 +127,18 @@ sub _sales_last_duration {
my ( $start, $end ) = $c->_get_start_end_duration( $duration ); my ( $start, $end ) = $c->_get_start_end_duration( $duration );
$data->{bounds} = {
min => $c->format_iso_datetime( $start ),
max => $c->format_iso_datetime( $end ),
};
while ( $start < $end ) { while ( $start < $end ) {
my $next_end = $start->clone->add( days => 1 ); my $next_end = $start->clone->add( days => 1 );
my $transactions = $entity->sales my $transactions = $entity->sales
->search_between( $start, $next_end ) ->search_between( $start, $next_end )
->get_column('value') ->get_column('value')
->sum || 0 + 0; ->sum || 0 + 0;
push @{ $data->{ labels } }, $start->day_name; push @{ $data->{ labels } }, $c->format_iso_datetime( $start );
push @{ $data->{ data } }, $transactions / 100000; push @{ $data->{ data } }, $transactions / 100000;
$start->add( days => 1 ); $start->add( days => 1 );
} }
@ -164,13 +164,18 @@ sub _purchases_last_duration {
my ( $start, $end ) = $c->_get_start_end_duration( $duration ); my ( $start, $end ) = $c->_get_start_end_duration( $duration );
$data->{bounds} = {
min => $c->format_iso_datetime( $start ),
max => $c->format_iso_datetime( $end ),
};
while ( $start < $end ) { while ( $start < $end ) {
my $next_end = $start->clone->add( days => 1 ); my $next_end = $start->clone->add( days => 1 );
my $transactions = $entity->purchases my $transactions = $entity->purchases
->search_between( $start, $next_end ) ->search_between( $start, $next_end )
->get_column('value') ->get_column('value')
->sum || 0 + 0; ->sum || 0 + 0;
push @{ $data->{ labels } }, $start->day_name; push @{ $data->{ labels } }, $c->format_iso_datetime( $start );
push @{ $data->{ data } }, $transactions / 100000; push @{ $data->{ data } }, $transactions / 100000;
$start->add( days => 1 ); $start->add( days => 1 );
} }

View file

@ -13,8 +13,11 @@ __PACKAGE__->result_source_instance->view_definition( qq/
SELECT "value", SELECT "value",
"distance", "distance",
"purchase_time", "purchase_time",
"buyer_id",
"seller_id",
DATE_TRUNC('hour', "purchase_time") AS "quantised_hours", DATE_TRUNC('hour', "purchase_time") AS "quantised_hours",
DATE_TRUNC('day', "purchase_time") AS "quantised_days" DATE_TRUNC('day', "purchase_time") AS "quantised_days",
DATE_TRUNC('week', "purchase_time") AS "quantised_weeks"
FROM "transactions" FROM "transactions"
/); /);

View file

@ -13,8 +13,11 @@ __PACKAGE__->result_source_instance->view_definition( qq/
SELECT "value", SELECT "value",
"distance", "distance",
"purchase_time", "purchase_time",
"buyer_id",
"seller_id",
DATETIME(STRFTIME('%Y-%m-%d %H:00:00',"purchase_time")) AS "quantised_hours", DATETIME(STRFTIME('%Y-%m-%d %H:00:00',"purchase_time")) AS "quantised_hours",
DATETIME(STRFTIME('%Y-%m-%d 00:00:00',"purchase_time")) AS "quantised_days" DATETIME(STRFTIME('%Y-%m-%d 00:00:00',"purchase_time")) AS "quantised_days",
DATETIME(STRFTIME('%Y-%m-%d 00:00:00',"purchase_time", 'weekday 1')) AS "quantised_weeks"
FROM "transactions" FROM "transactions"
/); /);

View file

@ -14,98 +14,57 @@ $framework->install_fixtures('users');
my $t = $framework->framework; my $t = $framework->framework;
my $schema = $t->app->schema; my $schema = $t->app->schema;
my $dtf = $schema->storage->datetime_parser;
my $org_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity; my $start = DateTime->today->subtract( hours => 12 );
my $user_result = $schema->resultset('User')->find({ email => 'test1@example.com' })->entity;
# create 40 days worth of data
for my $count ( 0 .. 40 ) {
my $trans_day = $start->clone->subtract( days => $count );
create_random_transaction( 'test1@example.com', $trans_day );
if ( $count % 2 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
if ( $count % 3 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
if ( $count % 4 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
}
my $session_key = $framework->login({ my $session_key = $framework->login({
email => 'test1@example.com', email => 'test1@example.com',
password => 'abc123', password => 'abc123',
}); });
$t->app->schema->resultset('Leaderboard')->create_new( 'monthly_total', DateTime->now->truncate(to => 'month' )->subtract( months => 1) ); #TODO be able to define start and end below in request
$t->post_ok('/api/stats' => json => { session_key => $session_key } ) $t->post_ok('/api/stats' => json => {
session_key => $session_key,
})
->status_is(200)->or($framework->dump_error) ->status_is(200)->or($framework->dump_error)
->json_is('/success', Mojo::JSON->true) ->json_is('/weeks', {
->json_is('/today_sum', 0) purchases => [ 8, 21, 19, 22, 20, 20, 8 ],
->json_is('/today_count', 0) })
->json_is('/week_sum', 0) ->json_is('/sectors', {
->json_is('/week_count', 0) sectors => ['A'],
->json_is('/month_sum', 0) purchases => [118],
->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 ) { sub create_random_transaction {
$user_result->create_related( 'purchases', { my $buyer = shift;
seller_id => $org_result->id, my $time = shift;
value => $_ * 100000,
my $buyer_result = $schema->resultset('User')->find({ email => $buyer })->entity;
my $seller_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity;
$schema->resultset('Transaction')->create({
buyer => $buyer_result,
seller => $seller_result,
value => 10 * 100000,
proof_image => 'a', proof_image => 'a',
purchase_time => $time,
}); });
} }
for ( 11 .. 20 ) {
$user_result->create_related( 'purchases', {
seller_id => $org_result->id,
value => $_ * 100000,
proof_image => 'a',
purchase_time => $dtf->format_datetime(DateTime->today()->subtract( days => 5 )),
});
}
for ( 21 .. 30 ) {
$user_result->create_related( 'purchases', {
seller_id => $org_result->id,
value => $_ * 100000,
proof_image => 'a',
purchase_time => $dtf->format_datetime(DateTime->today()->subtract( days => 25 )),
});
}
for ( 31 .. 40 ) {
$user_result->create_related( 'purchases', {
seller_id => $org_result->id,
value => $_ * 100000,
proof_image => 'a',
purchase_time => $dtf->format_datetime(DateTime->today()->subtract( days => 50 )),
});
}
for ( 41 .. 50 ) {
$org_result->create_related( 'purchases', {
seller_id => $org_result->id,
value => $_ * 100000,
proof_image => 'a',
purchase_time => $dtf->format_datetime(DateTime->today()->subtract( days => 50 )),
});
}
is $user_result->purchases->search({
purchase_time => {
-between => [
$dtf->format_datetime(DateTime->today()),
$dtf->format_datetime(DateTime->today()->add( days => 1 )),
],
},
})->get_column('value')->sum, 5500000, 'Got correct sum';
is $user_result->purchases->today_rs->get_column('value')->sum, 5500000, 'Got correct sum through rs';
$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('/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; done_testing;

136
t/api/v1/customer/graphs.t Normal file
View file

@ -0,0 +1,136 @@
use Mojo::Base -strict;
use FindBin qw/ $Bin /;
use Test::More;
use Mojo::JSON;
use Test::Pear::LocalLoop;
use DateTime;
my $framework = Test::Pear::LocalLoop->new(
etc_dir => "$Bin/../../../etc",
);
$framework->install_fixtures('users');
my $t = $framework->framework;
my $schema = $t->app->schema;
my $start = DateTime->today->subtract( hours => 12 );
# create 30 days worth of data
for my $count ( 0 .. 29 ) {
my $trans_day = $start->clone->subtract( days => $count );
create_random_transaction( 'test1@example.com', $trans_day );
if ( $count % 2 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
if ( $count % 3 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
if ( $count % 4 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
}
my $session_key = $framework->login({
email => 'test1@example.com',
password => 'abc123',
});
$t->post_ok('/api/v1/customer/graphs' => json => {
session_key => $session_key,
graph => 'total_last_week',
})
->status_is(200)->or($framework->dump_error)
->json_is('/graph', {
labels => [ map { $t->app->format_iso_datetime(
$start->clone->subtract( days => $_ )->subtract( hours => 12 )
) } reverse ( 0 .. 6 ) ],
bounds => {
min => $t->app->format_iso_datetime($start->clone->subtract( days => 6 )->subtract( hours => 12 ) ),
max => $t->app->format_iso_datetime($start->clone->add( hours => 12 )),
},
data => [ 20, 40, 20, 30, 30, 40, 10 ],
});
$t->post_ok('/api/v1/customer/graphs' => json => {
session_key => $session_key,
graph => 'avg_spend_last_week',
})
->status_is(200)->or($framework->dump_error)
->json_is('/graph', {
labels => [ map { $t->app->format_iso_datetime(
$start->clone->subtract( days => $_ )->subtract( hours => 12 )
) } reverse ( 0 .. 6 ) ],
bounds => {
min => $t->app->format_iso_datetime($start->clone->subtract( days => 6 )->subtract( hours => 12 ) ),
max => $t->app->format_iso_datetime($start->clone->add( hours => 12 )),
},
data => [ 10, 10, 10, 10, 10, 10, 10 ],
});
$t->post_ok('/api/v1/customer/graphs' => json => {
session_key => $session_key,
graph => 'total_last_month',
})
->status_is(200)->or($framework->dump_error)
->json_is('/graph', {
labels => [ map { $t->app->format_iso_datetime(
$start->clone->subtract( days => $_ )->subtract( hours => 12 )
) } reverse ( 0 .. 29 ) ],
bounds => {
min => $t->app->format_iso_datetime($start->clone->subtract( days => 29 )->subtract( hours => 12 ) ),
max => $t->app->format_iso_datetime($start->clone->add( hours => 12 )),
},
data => [ 40, 20, 30, 30, 40, 10, 40, 30, 30, 20, 40, 20, 40, 20, 30, 30, 40, 10, 40, 30, 30, 20, 40, 20, 40, 20, 30, 30, 40, 10 ],
});
$t->post_ok('/api/v1/customer/graphs' => json => {
session_key => $session_key,
graph => 'avg_spend_last_month',
})
->status_is(200)->or($framework->dump_error)
->json_is('/graph', {
labels => [ map { $t->app->format_iso_datetime(
$start->clone->subtract( days => $_ )->subtract( hours => 12 )
) } reverse ( 0 .. 29 ) ],
bounds => {
min => $t->app->format_iso_datetime($start->clone->subtract( days => 29 )->subtract( hours => 12 ) ),
max => $t->app->format_iso_datetime($start->clone->add( hours => 12 )),
},
data => [ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 ],
});
$framework->logout( $session_key );
$session_key = $framework->login({
email => 'org@example.com',
password => 'abc123',
});
$t->post_ok('/api/v1/customer/graphs' => json => {
session_key => $session_key,
graph => 'avg_spend_last_week',
})
->status_is(403)
->json_is('/success', Mojo::JSON->false)
->json_is('/error', 'user_not_cust');
sub create_random_transaction {
my $buyer = shift;
my $time = shift;
my $buyer_result = $schema->resultset('User')->find({ email => $buyer })->entity;
my $seller_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity;
$schema->resultset('Transaction')->create({
buyer => $buyer_result,
seller => $seller_result,
value => 10 * 100000,
proof_image => 'a',
purchase_time => $time,
});
}
done_testing;

67
t/api/v1/customer/pies.t Normal file
View file

@ -0,0 +1,67 @@
use Mojo::Base -strict;
use FindBin qw/ $Bin /;
use Test::More;
use Mojo::JSON;
use Test::Pear::LocalLoop;
use DateTime;
my $framework = Test::Pear::LocalLoop->new(
etc_dir => "$Bin/../../../etc",
);
$framework->install_fixtures('users');
my $t = $framework->framework;
my $schema = $t->app->schema;
my $start = DateTime->today->subtract( hours => 12 );
# create 30 days worth of data
for my $count ( 0 .. 29 ) {
my $trans_day = $start->clone->subtract( days => $count );
create_random_transaction( 'test1@example.com', $trans_day );
if ( $count % 2 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
if ( $count % 3 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
if ( $count % 4 ) {
create_random_transaction( 'test1@example.com', $trans_day );
}
}
my $session_key = $framework->login({
email => 'test1@example.com',
password => 'abc123',
});
$t->post_ok('/api/v1/customer/pies' => json => {
session_key => $session_key,
})
->status_is(200)->or($framework->dump_error)
->json_is('/pie', {
'Local shop local purchaser' => 0,
'Local shop non-local purchaser' => 0,
'Non-local shop local purchaser' => 0,
'Non-local shop non-local purchaser' => 0,
});
sub create_random_transaction {
my $buyer = shift;
my $time = shift;
my $buyer_result = $schema->resultset('User')->find({ email => $buyer })->entity;
my $seller_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity;
$schema->resultset('Transaction')->create({
buyer => $buyer_result,
seller => $seller_result,
value => 10 * 100000,
proof_image => 'a',
purchase_time => $time,
});
}
done_testing;

View file

@ -0,0 +1,74 @@
use Mojo::Base -strict;
use FindBin qw/ $Bin /;
use Test::More;
use Mojo::JSON;
use Test::Pear::LocalLoop;
use DateTime;
my $framework = Test::Pear::LocalLoop->new(
etc_dir => "$Bin/../../../etc",
);
$framework->install_fixtures('users');
my $t = $framework->framework;
my $schema = $t->app->schema;
$t->app->schema->resultset('Leaderboard')->create_new( 'monthly_total', DateTime->now->truncate(to => 'month' )->subtract( months => 1) );
my $start = DateTime->today->subtract( hours => 12 );
# create 30 days worth of data
for my $count ( 0 .. 60 ) {
my $trans_day = $start->clone->subtract( days => $count );
create_random_transaction( 'test1@example.com', $trans_day );
if ( $count % 2 ) {
create_random_transaction( 'test2@example.com', $trans_day );
}
if ( $count % 3 ) {
create_random_transaction( 'test3@example.com', $trans_day );
}
if ( $count % 4 ) {
create_random_transaction( 'test4@example.com', $trans_day );
}
}
my $session_key = $framework->login({
email => 'test1@example.com',
password => 'abc123',
});
$t->post_ok('/api/v1/customer/snippets' => json => {
session_key => $session_key,
})
->status_is(200)->or($framework->dump_error)
->json_is('/snippets', {
user_sum => 610,
user_position => 1,
});
$framework->logout( $session_key );
$session_key = $framework->login({
email => 'test1@example.com',
password => 'abc123',
});
sub create_random_transaction {
my $buyer = shift;
my $time = shift;
my $buyer_result = $schema->resultset('User')->find({ email => $buyer })->entity;
my $seller_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity;
$schema->resultset('Transaction')->create({
buyer => $buyer_result,
seller => $seller_result,
value => 10 * 100000,
proof_image => 'a',
purchase_time => $time,
});
}
done_testing;

View file

@ -44,7 +44,13 @@ $t->post_ok('/api/v1/organisation/graphs' => json => {
}) })
->status_is(200)->or($framework->dump_error) ->status_is(200)->or($framework->dump_error)
->json_is('/graph', { ->json_is('/graph', {
labels => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 6 ) ], labels => [ map { $t->app->format_iso_datetime(
$start->clone->subtract( days => $_ )->subtract( hours => 12 )
) } reverse ( 0 .. 6 ) ],
bounds => {
min => $t->app->format_iso_datetime($start->clone->subtract( days => 6 )->subtract( hours => 12 ) ),
max => $t->app->format_iso_datetime($start->clone->add( hours => 12 )),
},
data => [ 2, 4, 2, 3, 3, 4, 1 ], data => [ 2, 4, 2, 3, 3, 4, 1 ],
}); });
@ -54,7 +60,13 @@ $t->post_ok('/api/v1/organisation/graphs' => json => {
}) })
->status_is(200)->or($framework->dump_error) ->status_is(200)->or($framework->dump_error)
->json_is('/graph', { ->json_is('/graph', {
labels => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 29 ) ], labels => [ map { $t->app->format_iso_datetime(
$start->clone->subtract( days => $_ )->subtract( hours => 12 )
) } reverse ( 0 .. 29 ) ],
bounds => {
min => $t->app->format_iso_datetime($start->clone->subtract( days => 29 )->subtract( hours => 12 ) ),
max => $t->app->format_iso_datetime($start->clone->add( hours => 12 )),
},
data => [ 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 2, 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 2, 4, 2, 3, 3, 4, 1 ], data => [ 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 2, 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 2, 4, 2, 3, 3, 4, 1 ],
}); });
@ -64,7 +76,13 @@ $t->post_ok('/api/v1/organisation/graphs' => json => {
}) })
->status_is(200)->or($framework->dump_error) ->status_is(200)->or($framework->dump_error)
->json_is('/graph', { ->json_is('/graph', {
labels => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 6 ) ], labels => [ map { $t->app->format_iso_datetime(
$start->clone->subtract( days => $_ )->subtract( hours => 12 )
) } reverse ( 0 .. 6 ) ],
bounds => {
min => $t->app->format_iso_datetime($start->clone->subtract( days => 6 )->subtract( hours => 12 ) ),
max => $t->app->format_iso_datetime($start->clone->add( hours => 12 )),
},
data => [ 20, 40, 20, 30, 30, 40, 10 ], data => [ 20, 40, 20, 30, 30, 40, 10 ],
}); });
@ -74,7 +92,13 @@ $t->post_ok('/api/v1/organisation/graphs' => json => {
}) })
->status_is(200)->or($framework->dump_error) ->status_is(200)->or($framework->dump_error)
->json_is('/graph', { ->json_is('/graph', {
labels => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 29 ) ], labels => [ map { $t->app->format_iso_datetime(
$start->clone->subtract( days => $_ )->subtract( hours => 12 )
) } reverse ( 0 .. 29 ) ],
bounds => {
min => $t->app->format_iso_datetime($start->clone->subtract( days => 29 )->subtract( hours => 12 ) ),
max => $t->app->format_iso_datetime($start->clone->add( hours => 12 )),
},
data => [ 40, 20, 30, 30, 40, 10, 40, 30, 30, 20, 40, 20, 40, 20, 30, 30, 40, 10, 40, 30, 30, 20, 40, 20, 40, 20, 30, 30, 40, 10 ], data => [ 40, 20, 30, 30, 40, 10, 40, 30, 30, 20, 40, 20, 40, 20, 30, 30, 40, 10, 40, 30, 30, 20, 40, 20, 40, 20, 30, 30, 40, 10 ],
}); });

View file

@ -91,6 +91,7 @@ my $entity5 = {
street_name => 'Test Street', street_name => 'Test Street',
town => 'Lancaster', town => 'Lancaster',
postcode => 'LA1 1AA', postcode => 'LA1 1AA',
sector => 'A',
}, },
user => { user => {
email => 'org@example.com', email => 'org@example.com',
@ -124,4 +125,3 @@ $fixtures->dump({
schema => $schema, schema => $schema,
directory => "$Bin/../data/" . $data_set, directory => "$Bin/../data/" . $data_set,
}); });

View file

@ -7,7 +7,7 @@ $HASH1 = {
pending => 0, pending => 0,
postcode postcode
=> 'LA1 1AA', => 'LA1 1AA',
sector => undef, sector => 'A',
street_name street_name
=> 'Test Street', => 'Test Street',
submitted_by_id submitted_by_id

View file

@ -81,8 +81,10 @@
<input id="town" type="text" class="form-control" placeholder="Town" name="town" value="<%= $org_rs->town %>"> <input id="town" type="text" class="form-control" placeholder="Town" name="town" value="<%= $org_rs->town %>">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="town">Town</label> <label for="town">Sector</label>
<input id="town" type="sector" class="form-control" placeholder="Sector Area Code" name="sector" value="<%= $org_rs->sector %>"> <select id="sector" type="sector" class="form-control" placeholder="Sector Area Code" name="sector">
%= include 'partials/sector_options', selected_sector => $org_rs->sector || '';
</select>
</div> </div>
</div> </div>
% } else { % } else {