diff options
author | Struan Donald <struan@exo.org.uk> | 2017-11-30 15:23:22 +0000 |
---|---|---|
committer | Matthew Somerville <matthew-github@dracos.co.uk> | 2017-12-15 17:35:53 +0000 |
commit | 9931b8aac55df6baef69cee76f34d76bc08d293c (patch) | |
tree | 9b2ced85970854030f8ab34b8eef52535baf4fcc | |
parent | af7a457bfaed2ba143aaeccfc7ccf2b8d443e22d (diff) |
[fixmystreet.com] CSV download from marketing page
Limited to most recent 100 rows and not full data.
Factor the two CSV generations together.
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Dashboard.pm | 162 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Reports.pm | 39 | ||||
-rw-r--r-- | t/cobrand/fixmystreet.t | 126 |
3 files changed, 222 insertions, 105 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index e1183c247..90f3866ec 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -5,6 +5,7 @@ use namespace::autoclean; use DateTime; use JSON::MaybeXS; use Path::Tiny; +use Text::CSV; use Time::Piece; BEGIN { extends 'Catalyst::Controller'; } @@ -112,7 +113,7 @@ sub index : Path : Args(0) { $c->forward('construct_rs_filter'); if ( $c->get_param('export') ) { - $self->export_as_csv($c); + $c->forward('export_as_csv'); } else { $self->generate_data($c); } @@ -294,28 +295,15 @@ sub generate_body_response_time : Private { $c->stash->{body_average} = $avg ? int($avg / 60 / 60 / 24 + 0.5) : 0; } -sub export_as_csv { +sub export_as_csv : Private { my ($self, $c) = @_; - require Text::CSV; - my $problems = $c->stash->{problems_rs}->search( - {}, { prefetch => 'comments', order_by => 'me.confirmed' }); - - my $filename = do { - my %where = ( - category => $c->stash->{category}, - state => $c->stash->{q_state}, - ward => $c->stash->{ward}, - ); - $where{body} = $c->stash->{body}->id if $c->stash->{body}; - join '-', - $c->req->uri->host, - map { - my $value = $where{$_}; - (defined $value and length $value) ? ($_, $value) : () - } sort keys %where }; - my $csv = Text::CSV->new({ binary => 1, eol => "\n" }); - $csv->combine( + my $csv = $c->stash->{csv} = { + problems => $c->stash->{problems_rs}->search_rs({}, { + prefetch => 'comments', + order_by => 'me.confirmed' + }), + headers => [ 'Report ID', 'Title', 'Detail', @@ -333,68 +321,116 @@ sub export_as_csv { 'Easting', 'Northing', 'Report URL', + ], + columns => [ + 'id', + 'title', + 'detail', + 'user_name_display', + 'category', + 'created', + 'confirmed', + 'acknowledged', + 'fixed', + 'closed', + 'state', + 'latitude', 'longitude', + 'postcode', + 'wards', + 'local_coords_x', + 'local_coords_y', + 'url', + ], + filename => do { + my %where = ( + category => $c->stash->{category}, + state => $c->stash->{q_state}, + ward => $c->stash->{ward}, ); + $where{body} = $c->stash->{body}->id if $c->stash->{body}; + join '-', + $c->req->uri->host, + map { + my $value = $where{$_}; + (defined $value and length $value) ? ($_, $value) : () + } sort keys %where + }, + }; + $c->forward('generate_csv'); +} + +=head2 generate_csv + +Generates a CSV output, given a 'csv' stash hashref containing: +* filename: filename to be used in output +* problems: a resultset of the rows to output +* headers: an arrayref of the header row strings +* columns: an arrayref of the columns (looked up in the row's as_hashref, plus +the following: user_name_display, acknowledged, fixed, closed, wards, +local_coords_x, local_coords_y, url). + +=cut + +sub generate_csv : Private { + my ($self, $c) = @_; + + my $csv = Text::CSV->new({ binary => 1, eol => "\n" }); + $csv->combine(@{$c->stash->{csv}->{headers}}); my @body = ($csv->string); my $fixed_states = FixMyStreet::DB::Result::Problem->fixed_states; my $closed_states = FixMyStreet::DB::Result::Problem->closed_states; + my $wards = 0; + my $comments = 0; + foreach (@{$c->stash->{csv}->{columns}}) { + $wards = 1 if $_ eq 'wards'; + $comments = 1 if $_ eq 'acknowledged'; + } + + my $problems = $c->stash->{csv}->{problems}; while ( my $report = $problems->next ) { - my $external_body; - my $body_name = ""; - if ( $external_body = $report->body($c) ) { - # seems to be a zurich specific thing - $body_name = $external_body->name if ref $external_body; - } my $hashref = $report->as_hashref($c); - $hashref->{user_name_display} = $report->anonymous? - '(anonymous)' : $report->user->name; - - for my $comment ($report->comments) { - my $problem_state = $comment->problem_state or next; - next unless $comment->state eq 'confirmed'; - next if $problem_state eq 'confirmed'; - $hashref->{acknowledged} //= $comment->confirmed; - $hashref->{fixed} //= $fixed_states->{ $problem_state } || $comment->mark_fixed ? - $comment->confirmed : undef; - if ($closed_states->{ $problem_state }) { - $hashref->{closed} = $comment->confirmed; - last; + $hashref->{user_name_display} = $report->anonymous + ? '(anonymous)' : $report->user->name; + + if ($comments) { + for my $comment ($report->comments) { + my $problem_state = $comment->problem_state or next; + next unless $comment->state eq 'confirmed'; + next if $problem_state eq 'confirmed'; + $hashref->{acknowledged} //= $comment->confirmed; + $hashref->{fixed} //= $fixed_states->{ $problem_state } || $comment->mark_fixed ? + $comment->confirmed : undef; + if ($closed_states->{ $problem_state }) { + $hashref->{closed} = $comment->confirmed; + last; + } } } - my $wards = join ', ', - map { $c->stash->{children}->{$_}->{name} } - grep {$c->stash->{children}->{$_} } - split ',', $hashref->{areas}; + if ($wards) { + $hashref->{wards} = join ', ', + map { $c->stash->{children}->{$_}->{name} } + grep {$c->stash->{children}->{$_} } + split ',', $hashref->{areas}; + } - my @local_coords = $report->local_coords; + ($hashref->{local_coords_x}, $hashref->{local_coords_y}) = + $report->local_coords; + $hashref->{url} = join '', $c->cobrand->base_url_for_report($report), $report->url; $csv->combine( @{$hashref}{ - 'id', - 'title', - 'detail', - 'user_name_display', - 'category', - 'created', - 'confirmed', - 'acknowledged', - 'fixed', - 'closed', - 'state', - 'latitude', 'longitude', - 'postcode', - }, - $wards, - $local_coords[0], - $local_coords[1], - (join '', $c->cobrand->base_url_for_report($report), $report->url), + @{$c->stash->{csv}->{columns}} + }, ); push @body, $csv->string; } + + my $filename = $c->stash->{csv}->{filename}; $c->res->content_type('text/csv; charset=utf-8'); $c->res->header('content-disposition' => "attachment; filename=${filename}.csv"); $c->res->body( join "", @body ); diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 724fc7fbd..ec7a192b3 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -468,12 +468,51 @@ sub summary : Private { $c->forward('/dashboard/construct_rs_filter'); + if ( $c->get_param('csv') ) { + $c->detach('export_summary_csv'); + } + $c->forward('/dashboard/generate_grouped_data'); $c->forward('/dashboard/generate_body_response_time'); $c->stash->{template} = 'reports/summary.html'; } +sub export_summary_csv : Private { + my ( $self, $c ) = @_; + + $c->stash->{csv} = { + problems => $c->stash->{problems_rs}->search_rs({}, { + rows => 100, + order_by => { '-desc' => 'me.confirmed' }, + }), + headers => [ + 'Report ID', + 'Title', + 'Category', + 'Created', + 'Confirmed', + 'Status', + 'Latitude', 'Longitude', + 'Query', + 'Report URL', + ], + columns => [ + 'id', + 'title', + 'category', + 'created_pp', + 'confirmed_pp', + 'state', + 'latitude', 'longitude', + 'postcode', + 'url', + ], + filename => 'fixmystreet-data.csv', + }; + $c->forward('/dashboard/generate_csv'); +} + =head2 check_canonical_url Given an already found (case-insensitively) body, check what URL diff --git a/t/cobrand/fixmystreet.t b/t/cobrand/fixmystreet.t index b48593593..30d5765a2 100644 --- a/t/cobrand/fixmystreet.t +++ b/t/cobrand/fixmystreet.t @@ -23,48 +23,90 @@ FixMyStreet::override_config { TEST_DASHBOARD_DATA => $data, ALLOWED_COBRANDS => 'fixmystreet', }, sub { - # Not logged in, redirected - $mech->get_ok('/reports/Birmingham/summary'); - is $mech->uri->path, '/about/council-dashboard'; - - $mech->submit_form_ok({ with_fields => { username => 'someone@somewhere.example.org' }}); - $mech->content_contains('We will be in touch'); - # XXX Check email arrives - - $mech->log_in_ok('someone@somewhere.example.org'); - $mech->get_ok('/reports/Birmingham/summary'); - is $mech->uri->path, '/about/council-dashboard'; - $mech->content_contains('Ending in .gov.uk'); - - $mech->submit_form_ok({ with_fields => { name => 'Someone', username => 'someone@birmingham.gov.uk' }}); - $mech->content_contains('Now check your email'); - # XXX Check email arrives, click link - - $mech->log_in_ok('someone@birmingham.gov.uk'); - # Logged in, redirects - $mech->get_ok('/about/council-dashboard'); - is $mech->uri->path, '/reports/Birmingham/summary'; - $mech->content_contains('Where we send Birmingham'); - $mech->content_contains('lights@example.com'); - - $body->send_method('Open311'); - $body->update(); - $mech->get_ok('/about/council-dashboard'); - $mech->content_contains('Reports to Birmingham are currently sent directly'); - - $body->send_method('Refused'); - $body->update(); - $mech->get_ok('/about/council-dashboard'); - $mech->content_contains('Birmingham currently does not accept'); - - $body->send_method('Noop'); - $body->update(); - $mech->get_ok('/about/council-dashboard'); - $mech->content_contains('Reports are currently not being sent'); - - $mech->log_out_ok(); - $mech->get_ok('/reports'); - $mech->content_lacks('Where we send Birmingham'); + subtest 'check marketing dashboard access' => sub { + # Not logged in, redirected + $mech->get_ok('/reports/Birmingham/summary'); + is $mech->uri->path, '/about/council-dashboard'; + + $mech->submit_form_ok({ with_fields => { username => 'someone@somewhere.example.org' }}); + $mech->content_contains('We will be in touch'); + # XXX Check email arrives + + $mech->log_in_ok('someone@somewhere.example.org'); + $mech->get_ok('/reports/Birmingham/summary'); + is $mech->uri->path, '/about/council-dashboard'; + $mech->content_contains('Ending in .gov.uk'); + + $mech->submit_form_ok({ with_fields => { name => 'Someone', username => 'someone@birmingham.gov.uk' }}); + $mech->content_contains('Now check your email'); + # XXX Check email arrives, click link + + $mech->log_in_ok('someone@birmingham.gov.uk'); + # Logged in, redirects + $mech->get_ok('/about/council-dashboard'); + is $mech->uri->path, '/reports/Birmingham/summary'; + $mech->content_contains('Where we send Birmingham'); + $mech->content_contains('lights@example.com'); + }; + + subtest 'check marketing dashboard csv' => sub { + $mech->log_in_ok('someone@birmingham.gov.uk'); + $mech->create_problems_for_body(105, $body->id, 'Title', { + detail => "this report\nis split across\nseveral lines", + areas => ",2514,", + }); + + $mech->get_ok('/reports/Birmingham/summary?csv=1'); + open my $data_handle, '<', \$mech->content; + my $csv = Text::CSV->new( { binary => 1 } ); + my @rows; + while ( my $row = $csv->getline( $data_handle ) ) { + push @rows, $row; + } + is scalar @rows, 101, '1 (header) + 100 (reports) = 101 lines'; + + is scalar @{$rows[0]}, 10, '10 columns present'; + + is_deeply $rows[0], + [ + 'Report ID', + 'Title', + 'Category', + 'Created', + 'Confirmed', + 'Status', + 'Latitude', + 'Longitude', + 'Query', + 'Report URL', + ], + 'Column headers look correct'; + + my $body_id = $body->id; + like $rows[1]->[1], qr/Title Test \d+ for $body_id/, 'problem title correct'; + }; + + subtest 'check marketing dashboard contact listings' => sub { + $mech->log_in_ok('someone@birmingham.gov.uk'); + $body->send_method('Open311'); + $body->update(); + $mech->get_ok('/about/council-dashboard'); + $mech->content_contains('Reports to Birmingham are currently sent directly'); + + $body->send_method('Refused'); + $body->update(); + $mech->get_ok('/about/council-dashboard'); + $mech->content_contains('Birmingham currently does not accept'); + + $body->send_method('Noop'); + $body->update(); + $mech->get_ok('/about/council-dashboard'); + $mech->content_contains('Reports are currently not being sent'); + + $mech->log_out_ok(); + $mech->get_ok('/reports'); + $mech->content_lacks('Where we send Birmingham'); + }; }; END { |