diff options
author | Matthew Somerville <matthew@mysociety.org> | 2019-10-08 09:34:49 +0100 |
---|---|---|
committer | Matthew Somerville <matthew@mysociety.org> | 2019-10-15 09:06:43 +0100 |
commit | 448a603594f598bd692fa402139357d945c60d15 (patch) | |
tree | dabb16d5687a5977d6e03fc7b4e0d2decc9a2336 | |
parent | faeb8244d9281c064c832366257ac46d5143a9f1 (diff) |
Generalise heatmap code to be under /dashboard.
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Dashboard.pm | 101 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Reports.pm | 31 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Bromley.pm | 87 | ||||
-rw-r--r-- | t/cobrand/bromley.t | 5 | ||||
-rw-r--r-- | templates/web/base/admin/stats/index.html | 9 | ||||
-rw-r--r-- | templates/web/base/dashboard/heatmap-list.html (renamed from templates/web/bromley/about/heatmap-list.html) | 2 | ||||
-rwxr-xr-x | templates/web/base/dashboard/heatmap.html (renamed from templates/web/bromley/about/heatmap.html) | 11 | ||||
-rw-r--r-- | templates/web/base/reports/_list-filter-status.html | 2 | ||||
-rw-r--r-- | templates/web/bromley/footer_extra_js.html | 1 | ||||
-rw-r--r-- | web/cobrands/fixmystreet/density-map.js (renamed from web/cobrands/bromley/js.js) | 1 | ||||
-rw-r--r-- | web/vendor/HeatmapLayer.js (renamed from web/cobrands/bromley/HeatmapLayer.js) | 0 |
12 files changed, 143 insertions, 111 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4429bb95e..1fc1255dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Pass ‘filter_category’ param to front page to pre-filter map. - Remove on-map Permalink. - Darken front page step numbers, and improve nested heading structure. + - Set report title autocomplete to off to prevent email autocompleting - Admin improvements: - Add new roles system, to group permissions and apply to users. #2483 - Contact form emails now include user admin links. @@ -21,6 +22,7 @@ - New features: - Categories can be listed under more than one group #2475 - OpenID Connect login support. #2523 + - Heatmap dashboard. #2675 - Bugfixes: - Prevent creation of two templates with same title. #2471 - Fix bug going between report/new pages client side. #2484 @@ -38,8 +40,6 @@ - Allow contact send method to be unset always. - Fix z-index stacking bug that was causing unclickable RSS icons on /alert page. #2624 - Fix issue with inspector duplication workflow. - - Front end improvements: - - Set report title autocomplete to off to prevent email autocompleting - Development improvements: - Upgrade the underlying framework and a number of other packages. #2473 - Add feature cobrand helper function. diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index 60c9b0823..f226e08de 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -499,6 +499,107 @@ sub generate_csv : Private { } } +sub heatmap : Local : Args(0) { + my ($self, $c) = @_; + + my $body = $c->stash->{body} = $c->forward('check_page_allowed'); + $c->detach( '/page_error_404_not_found' ) + unless $body && $c->cobrand->feature('heatmap'); + + $c->stash->{page} = 'reports'; # So the map knows to make clickable pins + + my @wards = $c->get_param_list('wards', 1); + $c->forward('/reports/ward_check', [ @wards ]) if @wards; + $c->forward('/reports/stash_report_filter_status'); + $c->forward('/reports/stash_report_sort', [ $c->cobrand->reports_ordering ]); # Not actually used + my $parameters = $c->forward( '/reports/load_problems_parameters'); + + my $where = $parameters->{where}; + my $filter = $parameters->{filter}; + delete $filter->{rows}; + + $c->forward('heatmap_filters', [ $where ]); + + # Load the relevant stuff for the sidebar as well + my $problems = $c->cobrand->problems; + $problems = $problems->to_body($body); + $problems = $problems->search($where, $filter); + + $c->forward('heatmap_sidebar', [ $problems, $where ]); + + if ($c->get_param('ajax')) { + my @pins; + while ( my $problem = $problems->next ) { + push @pins, $problem->pin_data($c, 'reports'); + } + $c->stash->{pins} = \@pins; + $c->detach('/reports/ajax', [ 'dashboard/heatmap-list.html' ]); + } + + my $children = $c->stash->{body}->first_area_children; + $c->stash->{children} = $children; + $c->stash->{ward_hash} = { map { $_->{id} => 1 } @{$c->stash->{wards}} } if $c->stash->{wards}; + + $c->forward('/reports/setup_categories_and_map'); +} + +sub heatmap_filters :Private { + my ($self, $c, $where) = @_; + + # Wards + my @areas = @{$c->user->area_ids || []}; + # Want to get everything if nothing given in an ajax call + if (!$c->stash->{wards} && @areas) { + $c->stash->{wards} = [ map { { id => $_ } } @areas ]; + $where->{areas} = [ + map { { 'like', '%,' . $_ . ',%' } } @areas + ]; + } + + # Date range + my $start_default = DateTime->today(time_zone => FixMyStreet->time_zone || FixMyStreet->local_time_zone)->subtract(months => 1); + $c->stash->{start_date} = $c->get_param('start_date') || $start_default->strftime('%Y-%m-%d'); + $c->stash->{end_date} = $c->get_param('end_date'); + + my $range = FixMyStreet::DateRange->new( + start_date => $c->stash->{start_date}, + start_default => $start_default, + end_date => $c->stash->{end_date}, + formatter => $c->model('DB')->storage->datetime_parser, + ); + $where->{'me.confirmed'} = $range->sql; +} + +sub heatmap_sidebar :Private { + my ($self, $c, $problems, $where) = @_; + + $c->stash->{five_newest} = [ $problems->search(undef, { + rows => 5, + order_by => { -desc => 'confirmed' }, + })->all ]; + + $c->stash->{ten_oldest} = [ $problems->search({ + 'me.state' => [ FixMyStreet::DB::Result::Problem->open_states() ], + }, { + rows => 10, + order_by => 'lastupdate', + })->all ]; + + my $params = { map { my $n = $_; s/me\./problem\./; $_ => $where->{$n} } keys %$where }; + my $body = $c->stash->{body}; + my @c = $c->model('DB::Comment')->to_body($body)->search({ + %$params, + 'me.user_id' => { -not_in => [ $c->user->id, $body->comment_user_id || () ] }, + 'me.state' => 'confirmed', + }, { + columns => 'problem_id', + group_by => 'problem_id', + order_by => { -desc => \'max(me.confirmed)' }, + rows => 5, + })->all; + $c->stash->{five_commented} = [ map { $_->problem } @c ]; +} + =head1 AUTHOR Matthew Somerville diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 771d829f0..85e647f65 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -560,29 +560,23 @@ sub load_and_group_problems : Private { my $parameters = $c->forward('load_problems_parameters'); - # JS will request the same (or more) data client side - return if $c->get_param('js'); - my $body = $c->stash->{body}; # Might be undef my $page = $c->get_param('p') || 1; - my $problems = $parameters->{problems}; - unless ($problems) { - my $where = $parameters->{where}; - my $filter = $parameters->{filter}; + my $problems = $c->cobrand->problems; + my $where = $parameters->{where}; + my $filter = $parameters->{filter}; - $problems = $c->cobrand->problems; - if ($where->{areas} || $body) { - $problems = $problems->to_body($body); - } + if ($where->{areas} || $body) { + $problems = $problems->to_body($body); + } - $problems = $problems->search( - $where, - $filter - )->include_comment_counts->page( $page ); + $problems = $problems->search( + $where, + $filter + )->include_comment_counts->page( $page ); - $c->stash->{pager} = $problems->pager; - } + $c->stash->{pager} = $problems->pager; my ( %problems, @pins ); while ( my $problem = $problems->next ) { @@ -667,10 +661,9 @@ sub load_problems_parameters : Private { $where->{longitude} = { '>=', $min_lon, '<', $max_lon }; } - my $cobrand_problems = $c->cobrand->call_hook('munge_load_and_group_problems', $where, $filter); + $c->cobrand->call_hook('munge_load_and_group_problems', $where, $filter); return { - problems => $cobrand_problems, where => $where, filter => $filter, }; diff --git a/perllib/FixMyStreet/Cobrand/Bromley.pm b/perllib/FixMyStreet/Cobrand/Bromley.pm index 391106346..29ff4393f 100644 --- a/perllib/FixMyStreet/Cobrand/Bromley.pm +++ b/perllib/FixMyStreet/Cobrand/Bromley.pm @@ -127,16 +127,9 @@ sub tweak_all_reports_map { } # A place where this can happen - return unless $c->stash->{template} && $c->stash->{template} eq 'about/heatmap.html'; - - my $children = $c->stash->{body}->first_area_children; - foreach (values %$children) { - $_->{url} = $c->uri_for( $c->stash->{body_url} - . '/' . $c->cobrand->short_name( $_ ) - ); - } - $c->stash->{children} = $children; + return unless $c->action eq 'dashboard/heatmap'; + # Bromley only subcategory stuff my %subcats = $self->subcategories; my $filter = $c->stash->{filter_categories}; my @new_contacts; @@ -156,8 +149,6 @@ sub tweak_all_reports_map { my $subcats = $c->user->get_extra_metadata('subcategories') || []; $c->stash->{filter_category} = { map { $_ => 1 } @$cats, @$subcats } if @$cats || @$subcats; } - - $c->stash->{ward_hash} = { map { $_->{id} => 1 } @{$c->stash->{wards}} } if $c->stash->{wards}; } sub title_list { @@ -331,27 +322,14 @@ sub add_admin_subcategories { return \@new_contacts; } -sub about_hook { - my $self = shift; - my $c = $self->{c}; - - # Display a special custom dashboard page, with heatmap - if ($c->stash->{template} eq 'about/heatmap.html') { - $c->forward('/dashboard/check_page_allowed'); - # We want a special sidebar - $c->stash->{ajax_template} = "about/heatmap-list.html"; - $c->set_param('js', 1) unless $c->get_param('ajax'); # Want to load pins client-side - $c->forward('/reports/body', [ 'Bromley' ]); - } -} - -# On heatmap page, include querying on subcategories, wards, dates, provided +# On heatmap page, include querying on subcategories sub munge_load_and_group_problems { my ($self, $where, $filter) = @_; my $c = $self->{c}; - return unless $c->stash->{template} && $c->stash->{template} eq 'about/heatmap.html'; + return unless $c->action eq 'dashboard/heatmap'; + # Bromley subcategory stuff if (!$where->{category}) { my $cats = $c->user->categories; my $subcats = $c->user->get_extra_metadata('subcategories') || []; @@ -370,61 +348,6 @@ sub munge_load_and_group_problems { }; delete $where->{category}; } - - # Wards - my @areas = @{$c->user->area_ids || []}; - # Want to get everything if nothing given in an ajax call - if (!$c->stash->{wards} && @areas) { - $c->stash->{wards} = [ map { { id => $_ } } @areas ]; - $where->{areas} = [ - map { { 'like', '%,' . $_ . ',%' } } @areas - ]; - } - - # Date range - my $start_default = DateTime->today(time_zone => FixMyStreet->time_zone || FixMyStreet->local_time_zone)->subtract(months => 1); - $c->stash->{start_date} = $c->get_param('start_date') || $start_default->strftime('%Y-%m-%d'); - $c->stash->{end_date} = $c->get_param('end_date'); - - my $range = FixMyStreet::DateRange->new( - start_date => $c->stash->{start_date}, - start_default => $start_default, - end_date => $c->stash->{end_date}, - formatter => $c->model('DB')->storage->datetime_parser, - ); - $where->{'me.confirmed'} = $range->sql; - - delete $filter->{rows}; - - # Load the relevant stuff for the sidebar as well - my $problems = $self->problems->search($where, $filter); - - $c->stash->{five_newest} = [ $problems->search(undef, { - rows => 5, - order_by => { -desc => 'confirmed' }, - })->all ]; - - $c->stash->{ten_oldest} = [ $problems->search({ - 'me.state' => [ FixMyStreet::DB::Result::Problem->open_states() ], - }, { - rows => 10, - order_by => 'lastupdate', - })->all ]; - - my $params = { map { my $n = $_; s/me\./problem\./; $_ => $where->{$n} } keys %$where }; - my @c = $c->model('DB::Comment')->to_body($self->body)->search({ - %$params, - 'me.user_id' => { -not_in => [ $c->user->id, $self->body->comment_user_id || () ] }, - 'me.state' => 'confirmed', - }, { - columns => 'problem_id', - group_by => 'problem_id', - order_by => { -desc => \'max(me.confirmed)' }, - rows => 5, - })->all; - $c->stash->{five_commented} = [ map { $_->problem } @c ]; - - return $problems; } 1; diff --git a/t/cobrand/bromley.t b/t/cobrand/bromley.t index 6750d3183..3ab1f6150 100644 --- a/t/cobrand/bromley.t +++ b/t/cobrand/bromley.t @@ -233,10 +233,11 @@ subtest 'check heatmap page' => sub { FixMyStreet::override_config { ALLOWED_COBRANDS => 'bromley', MAPIT_URL => 'http://mapit.uk/', + COBRAND_FEATURES => { heatmap => { bromley => 1 } }, }, sub { $mech->log_in_ok($user->email); - $mech->get_ok('/about/heatmap?end_date=2018-12-31'); - $mech->get_ok('/about/heatmap?filter_category=RED&ajax=1'); + $mech->get_ok('/dashboard/heatmap?end_date=2018-12-31'); + $mech->get_ok('/dashboard/heatmap?filter_category=RED&ajax=1'); }; }; diff --git a/templates/web/base/admin/stats/index.html b/templates/web/base/admin/stats/index.html index d47054427..452af001e 100644 --- a/templates/web/base/admin/stats/index.html +++ b/templates/web/base/admin/stats/index.html @@ -5,6 +5,15 @@ <li><a href="[% c.uri_for_action('admin/stats/state') %]">[% loc('Problem breakdown by state') %]</a></li> <li><a href="[% c.uri_for_action('admin/stats/fix_rate') %]">[% loc('Category fix rate for problems > 4 weeks old') %]</a></li> <li><a href="[% c.uri_for_action('dashboard/index') %]">[% loc('Summary statistics') %]</a></li> +[% IF c.cobrand.feature('heatmap').keys %] + <li>[% loc('Heatmap') %] <ul> + [% FOR body IN c.cobrand.feature('heatmap').pairs %] + <li><a href="[% c.uri_for_action('dashboard/heatmap') %]?body=[% body.value %]">[% body.key %]</a></li> + [% END %] + </ul> </li> +[% ELSIF c.cobrand.feature('heatmap') %] +<li><a href="[% c.uri_for_action('dashboard/heatmap') %]">[% loc('Heatmap') %]</a></li> +[% END %] [% IF c.cobrand.moniker == 'fixmystreet' %] <li><a href="[% c.uri_for_action('admin/stats/refused') %]">Refused</a></li> [% END %] diff --git a/templates/web/bromley/about/heatmap-list.html b/templates/web/base/dashboard/heatmap-list.html index e04df8581..d736a11f4 100644 --- a/templates/web/bromley/about/heatmap-list.html +++ b/templates/web/base/dashboard/heatmap-list.html @@ -2,7 +2,7 @@ [% INCLUDE column problems = five_newest %] <h3>Five most recent commented reports<br> -<small>Not from yourself or Confirm</small> +<small>Not from yourself/backend</small> </h3> [% INCLUDE column problems = five_commented %] diff --git a/templates/web/bromley/about/heatmap.html b/templates/web/base/dashboard/heatmap.html index 0cb8bfb1b..d180af09a 100755 --- a/templates/web/bromley/about/heatmap.html +++ b/templates/web/base/dashboard/heatmap.html @@ -1,5 +1,8 @@ [% - map_js.push(version('/cobrands/bromley/HeatmapLayer.js')); + map_js.push( + '/vendor/HeatmapLayer.js', + '/cobrands/fixmystreet/density-map.js', + ); PROCESS "maps/${map.type}.html"; SET bodyclass = 'mappage'; INCLUDE 'header.html', @@ -18,7 +21,9 @@ <div class="full-width"> -[% INCLUDE "reports/_list-filters.html", use_form_wrapper = 1 %] +[% INCLUDE "reports/_list-filters.html", use_form_wrapper = 1 heatmap=1 %] + +<input type="hidden" id="body" name="body" value="[% body.id | html %]"> <p class="report-list-filters" style='padding-top:0.25em'> From <input type="date" id="start_date" name="start_date" class="form-control" value="[% start_date | html %]"> @@ -42,7 +47,7 @@ In wards <select class="form-control js-multiple" multiple id="wards" name="ward </p> <div id="js-reports-list"> - [% INCLUDE 'about/heatmap-list.html' %] + [% INCLUDE 'dashboard/heatmap-list.html' %] </div> </div> diff --git a/templates/web/base/reports/_list-filter-status.html b/templates/web/base/reports/_list-filter-status.html index 6923a7929..6fa998fc1 100644 --- a/templates/web/base/reports/_list-filter-status.html +++ b/templates/web/base/reports/_list-filter-status.html @@ -20,7 +20,7 @@ [%~ END ~%] [% INCLUDE 'reports/_status_filter_options.html' %] > - [% IF c.user_exists AND c.user.has_body_permission_to('planned_reports') AND !shortlist %] + [% IF c.user_exists AND c.user.has_body_permission_to('planned_reports') AND !shortlist AND !heatmap %] <option value="shortlisted"[% ' selected' IF filter_status.shortlisted %]>[% loc('Shortlisted') %]</option> <option value="unshortlisted"[% ' selected' IF filter_status.unshortlisted %]>[% loc('Unshortlisted') %]</option> [% END %] diff --git a/templates/web/bromley/footer_extra_js.html b/templates/web/bromley/footer_extra_js.html index 26c8fe9f5..b58dda570 100644 --- a/templates/web/bromley/footer_extra_js.html +++ b/templates/web/bromley/footer_extra_js.html @@ -5,7 +5,6 @@ ) %] [% IF bodyclass.match('mappage'); scripts.push( - version('/cobrands/bromley/js.js'), version('/vendor/OpenLayers.Projection.OrdnanceSurvey.js'), version('/cobrands/fixmystreet/assets.js'), version('/cobrands/bromley/map.js'), diff --git a/web/cobrands/bromley/js.js b/web/cobrands/fixmystreet/density-map.js index 8ff314189..cbbda8a27 100644 --- a/web/cobrands/bromley/js.js +++ b/web/cobrands/fixmystreet/density-map.js @@ -19,6 +19,7 @@ if (window.Heatmap) { fixmystreet.protocol_params.wards = 'wards'; fixmystreet.protocol_params.start_date = 'start_date'; fixmystreet.protocol_params.end_date = 'end_date'; +fixmystreet.protocol_params.body = 'body'; $(function(){ if (!window.Heatmap) { diff --git a/web/cobrands/bromley/HeatmapLayer.js b/web/vendor/HeatmapLayer.js index 93f3e84b2..93f3e84b2 100644 --- a/web/cobrands/bromley/HeatmapLayer.js +++ b/web/vendor/HeatmapLayer.js |