aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Somerville <matthew@mysociety.org>2011-05-19 12:46:39 +0100
committerMatthew Somerville <matthew@mysociety.org>2011-05-19 12:46:39 +0100
commitd44c6e58d1efe505006a38a62b61b1045031dd49 (patch)
treeb9d453c4ca0ae8b3d869d82ec4f1607ff0d7666c
parentd8534c980d298764a0956fb803e7b23f496e9a30 (diff)
Migrate /reports page (minus RSS).
-rw-r--r--conf/httpd.conf12
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm1
-rw-r--r--perllib/FixMyStreet/App/Controller/Reports.pm361
-rw-r--r--perllib/FixMyStreet/Cobrand/Barnet.pm2
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm22
-rw-r--r--perllib/FixMyStreet/Cobrand/FiksGataMi.pm33
-rw-r--r--perllib/FixMyStreet/DB/Result/Problem.pm25
-rw-r--r--perllib/Problems.pm7
-rwxr-xr-xtemplates/web/default/reports/council.html99
-rwxr-xr-xtemplates/web/default/reports/index.html35
-rwxr-xr-xtemplates/web/default/reports/ward.html1
-rwxr-xr-xtemplates/web/emptyhomes/reports/index.html33
-rwxr-xr-xtemplates/web/fiksgatami/reports/index.html34
-rw-r--r--templates/website/reports12
-rwxr-xr-xweb/reports.cgi341
15 files changed, 656 insertions, 362 deletions
diff --git a/conf/httpd.conf b/conf/httpd.conf
index f1125e696..d9e6c470d 100644
--- a/conf/httpd.conf
+++ b/conf/httpd.conf
@@ -72,12 +72,12 @@ RewriteRule ^/rss/problems$ /rss.cgi?type=new_probl
# RSS feeds for voting areas
RewriteRule ^/rss/council/([0-9]+)$ /rss/reports/$1 [R=permanent,L]
RewriteRule ^/report$ /reports [R=permanent,L]
-RewriteRule ^/reports/([^/]+)/all$ /reports.cgi?council=$1;all=1 [QSA,L]
-RewriteRule ^/reports/([^/]+)/([^/]+)$ /reports.cgi?council=$1;ward=$2 [QSA,L]
-RewriteRule ^/rss/(reports|area)/([^/]+)/([^/]+)$ /reports.cgi?rss=$1;council=$2;ward=$3 [QSA,L]
-RewriteRule ^/reports/([^/]+)$ /reports.cgi?council=$1 [QSA,L]
+#RewriteRule ^/reports/([^/]+)/all$ /reports.cgi?council=$1;all=1 [QSA,L]
+#RewriteRule ^/reports/([^/]+)/([^/]+)$ /reports.cgi?council=$1;ward=$2 [QSA,L]
+#RewriteRule ^/rss/(reports|area)/([^/]+)/([^/]+)$ /reports.cgi?rss=$1;council=$2;ward=$3 [QSA,L]
+#RewriteRule ^/reports/([^/]+)$ /reports.cgi?council=$1 [QSA,L]
RewriteRule ^/rss/area/([0-9]+)$ /rss.cgi?type=area_problems;id=$1 [QSA,L]
-RewriteRule ^/rss/(reports|area)/([^/]+)$ /reports.cgi?rss=$1;council=$2 [QSA,L]
+#RewriteRule ^/rss/(reports|area)/([^/]+)$ /reports.cgi?rss=$1;council=$2 [QSA,L]
# Fix incorrect RSS urls caused by my stupidity
RewriteRule ^/{/rss/(.*)}$ /rss/$1 [R=permanent,L]
@@ -113,7 +113,7 @@ RewriteRule ^/fun(.*) /fun.cgi$1 [L]
RewriteRule ^/json(.*) /json.cgi$1 [L]
RewriteRule ^/photo(.*) /photo.cgi$1 [L]
RewriteRule ^/questionnaire(.*) /questionnaire.cgi$1 [L]
-RewriteRule ^/reports(.*) /reports.cgi$1 [L]
+# RewriteRule ^/reports(.*) /reports.cgi$1 [L]
RewriteRule ^/rss(.*) /rss.cgi$1 [L]
RewriteRule ^/test(.*) /test.cgi$1 [L]
RewriteRule ^/tms-signup(.*) /tms-signup.cgi$1 [L]
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index 1e1c6c974..ff4bb080c 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -238,6 +238,7 @@ sub generate_map_tags : Private {
post => $map_links
);
$c->stash->{map_end_html} = FixMyStreet::Map::display_map_end(0),
+ $c->stash->{map_js} = FixMyStreet::Map::header_js();
return 1;
}
diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm
new file mode 100644
index 000000000..228d4dd60
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Reports.pm
@@ -0,0 +1,361 @@
+package FixMyStreet::App::Controller::Reports;
+use Moose;
+use namespace::autoclean;
+
+use Problems;
+use POSIX qw(strcoll);
+# use FixMyStreet::Alert;
+use mySociety::MaPit;
+use mySociety::VotingArea;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Reports - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+=head2 index
+
+Show the summary page of all reports.
+
+=cut
+
+sub index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->response->header('Cache-Control' => 'max-age=3600');
+
+ # Fetch all areas of the types we're interested in
+ my @area_types = $c->cobrand->area_types;
+ my $areas_info = mySociety::MaPit::call('areas', \@area_types,
+ min_generation => $c->cobrand->area_min_generation
+ );
+
+ # For each area, add its link and perhaps alter its name if we need to for
+ # places with the same name.
+ foreach (values %$areas_info) {
+ $_->{url} = $c->uri_for( '/reports/' . $c->cobrand->short_name( $_, $areas_info ) );
+ if ($_->{parent_area} && $_->{url} =~ /,|%2C/) {
+ $_->{name} .= ', ' . $areas_info->{$_->{parent_area}}{name};
+ }
+ }
+
+ $c->stash->{areas_info} = $areas_info;
+ my @keys = sort { strcoll($areas_info->{$a}{name}, $areas_info->{$b}{name}) } keys %$areas_info;
+ $c->stash->{areas_info_sorted} = [ map { $areas_info->{$_} } @keys ];
+
+ $c->forward( 'load_problems' );
+ $c->forward( 'group_problems' );
+}
+
+=head2 index
+
+Show the summary page for a particular council.
+
+=cut
+
+sub council : Path : Args(1) {
+ my ( $self, $c, $council ) = @_;
+
+ $c->forward( 'council_check', [ $council ] );
+ $c->forward( 'load_parent' );
+ $c->forward( 'load_problems' );
+ $c->forward( 'group_problems' );
+ $c->forward( 'sort_problems' );
+
+ $c->stash->{rss_url} = '/rss/reports/'
+ . $c->cobrand->short_name( $c->stash->{council}, $c->stash->{areas_info} );
+}
+
+=head2 index
+
+Show the summary page for a particular ward.
+
+=cut
+
+sub ward : Path : Args(2) {
+ my ( $self, $c, $council, $ward ) = @_;
+
+ $c->forward( 'council_check', [ $council ] );
+ $c->forward( 'ward_check', [ $ward ] );
+ $c->forward( 'load_parent' );
+ $c->forward( 'load_problems' );
+ $c->forward( 'group_problems' );
+ $c->forward( 'sort_problems' );
+
+ $c->stash->{rss_url} = '/rss/reports/'
+ . $c->cobrand->short_name( $c->stash->{council}, $c->stash->{areas_info} )
+ . '/' . $c->cobrand->short_name( $c->stash->{ward} );
+}
+
+#sub rss_ward : RegEx('/rss/(reports|area)') : Args(2) {
+# my ( $self, $c, $council, $ward ) = @_;
+#
+# $c->stash->{q_council} = $council;
+# $c->stash->{q_ward} = $ward;
+#
+#}
+#
+#sub rss_council : RegEx('/rss/(reports|area)') : Args(1) {
+# my ( $self, $c, $council ) = @_;
+# $c->stash->{rss} = $c->req->{rss};
+#}
+
+=head2 council_check
+
+This action checks the council name (or code) given in a URI exists, is valid
+and so on. If it is, it stores the Area in the stash, otherwise it redirects
+to the all reports page.
+
+=cut
+
+sub council_check : Private {
+ my ( $self, $c, $q_council ) = @_;
+
+ $q_council =~ s/\+/ /g;
+
+ # Manual misspelling redirect
+ if ($q_council =~ /^rhondda cynon taff$/i) {
+ my $url = $c->uri_for( '/reports/rhondda+cynon+taf' );
+ $c->res->redirect( $url );
+ $c->detach();
+ }
+
+ # Check cobrand specific incantations - e.g. ONS codes for UK,
+ # Oslo/ kommunes sharing a name in Norway
+ return if $c->cobrand->reports_council_check( $c, $q_council );
+
+ # If we're passed an ID number (don't think this is used anywhere, it
+ # certainly shouldn't be), just look that up on MaPit and redirect
+ if ($q_council =~ /^\d+$/) {
+ my $council = mySociety::MaPit::call('area', $q_council);
+ $c->detach( 'redirect_index') if $council->{error};
+ $c->stash->{council} = $council;
+ $c->detach( 'redirect_council' );
+ }
+
+ # We must now have a string to check
+ my @area_types = $c->cobrand->area_types;
+ my $areas = mySociety::MaPit::call( 'areas', $q_council,
+ type => \@area_types,
+ min_generation => $c->cobrand->area_min_generation
+ );
+ if (keys %$areas == 1) {
+ ($c->stash->{council}) = values %$areas;
+ return;
+ } else {
+ foreach (keys %$areas) {
+ if ($areas->{$_}->{name} eq $q_council || $areas->{$_}->{name} =~ /^\Q$q_council\E (Borough|City|District|County) Council$/) {
+ $c->stash->{council} = $areas->{$_};
+ return;
+ }
+ }
+ }
+
+ # No result, bad council name.
+ $c->detach( 'redirect_index' );
+}
+
+=head2 ward_check
+
+This action checks the ward name from a URI exists and is part of the right
+parent, already found with council_check. It either stores the ward Area if
+okay, or redirects to the council page if bad.
+This is currently only used in the UK, hence the use of mySociety::VotingArea.
+
+=cut
+
+sub ward_check : Private {
+ my ( $self, $c, $ward ) = @_;
+
+ $ward =~ s/\+/ /g;
+ my $council = $c->stash->{council};
+
+ my $qw = mySociety::MaPit::call('areas', $ward,
+ type => $mySociety::VotingArea::council_child_types,
+ min_generation => $c->cobrand->area_min_generation
+ );
+ foreach my $id (sort keys %$qw) {
+ if ($qw->{$id}->{parent_area} == $council->{id}) {
+ $c->stash->{ward} = $qw->{$id};
+ return;
+ }
+ }
+ # Given a false ward name
+ $c->detach( 'redirect_council' );
+}
+
+sub load_parent : Private {
+ my ( $self, $c ) = @_;
+
+ my $council = $c->stash->{council};
+ my $areas_info;
+ if ($council->{parent_area}) {
+ $c->stash->{areas_info} = mySociety::MaPit::call('areas', [ $council->{id}, $council->{parent_area} ])
+ } else {
+ $c->stash->{areas_info} = { $council->{id} => $council };
+ }
+}
+
+sub load_problems : Private {
+ my ( $self, $c ) = @_;
+
+ my $where = {
+ state => [ 'confirmed', 'fixed' ],
+ %{ Problems::site_restriction() }
+ };
+ if ($c->stash->{ward}) {
+ $where->{areas} = { 'like', '%' . $c->stash->{ward}->{id} . '%' }; # FIXME Check this is secure
+ } elsif ($c->stash->{council}) {
+ $where->{areas} = { 'like', '%' . $c->stash->{council}->{id} . '%' };
+ }
+ my $current_timestamp = Problems::current_timestamp();
+ my $problems = $c->model('DB::Problem')->search(
+ $where,
+ {
+ columns => [
+ 'id', 'title', 'detail', 'council', 'state', 'areas',
+ { duration => { extract => "epoch from $current_timestamp-lastupdate" } },
+ { age => { extract => "epoch from $current_timestamp-confirmed" } },
+ ],
+ order_by => { -desc => 'id' },
+ }
+ );
+ $c->stash->{problems} = [ $problems->all ];
+
+ return 1;
+}
+
+sub group_problems : Private {
+ my ( $self, $c ) = @_;
+
+ my ( %fixed, %open );
+ my $re_councils = join('|', keys %{$c->stash->{areas_info}});
+ foreach my $row (@{$c->stash->{problems}}) {
+ if (!$row->council) {
+ # Problem was not sent to any council, add to possible councils
+ while ($row->areas =~ /,($re_councils)(?=,)/g) {
+ add_row($row, $1, \%fixed, \%open);
+ }
+ } else {
+ # Add to councils it was sent to
+ foreach ($row->councils) {
+ next if $c->stash->{council} && $_ != $c->stash->{council}->{id};
+ add_row($row, $_, \%fixed, \%open);
+ }
+ }
+ }
+
+ $c->stash->{fixed} = \%fixed;
+ $c->stash->{open} = \%open;
+
+ return 1;
+}
+
+sub sort_problems : Private {
+ my ( $self, $c ) = @_;
+
+ my $id = $c->stash->{council}->{id};
+ my $fixed = $c->stash->{fixed};
+ my $open = $c->stash->{open};
+
+ foreach (qw/new old/) {
+ $c->stash->{fixed}{$id}{$_} = [ sort { $a->{duration} <=> $b->{duration} } @{$fixed->{$id}{$_}} ]
+ if $fixed->{$id}{$_};
+ }
+ foreach (qw/new older unknown/) {
+ $c->stash->{open}{$id}{$_} = [ sort { $a->{age} <=> $b->{age} } @{$open->{$id}{$_}} ]
+ if $open->{$id}{$_};
+ }
+}
+
+sub redirect_index : Private {
+ my ( $self, $c ) = @_;
+ my $url = '/reports';
+ $c->res->redirect( $c->uri_for($url) );
+}
+
+sub redirect_council : Private {
+ my ( $self, $c ) = @_;
+ my $url = '';
+ $url .= "/rss" if $c->stash->{rss};
+ $url .= '/reports';
+ $url .= '/' . $c->cobrand->short_name( $c->stash->{council} );
+ $c->res->redirect( $c->uri_for($url) );
+}
+
+sub redirect_ward : Private {
+ my ( $self, $c ) = @_;
+ my $url = '';
+ $url .= "/rss" if $c->stash->{rss};
+ $url .= '/reports';
+ $url .= '/' . $c->cobrand->short_name( $c->stash->{council} );
+ $url .= '/' . $c->cobrand->short_name( $c->stash->{ward} );
+ $c->res->redirect( $c->uri_for($url) );
+}
+
+sub add_row {
+ my ($row, $council, $fixed, $open) = @_;
+ my $fourweeks = 4*7*24*60*60;
+ my $duration = ($row->get_column('duration') > 2 * $fourweeks) ? 'old' : 'new';
+ my $type = ($row->get_column('duration') > 2 * $fourweeks)
+ ? 'unknown'
+ : ($row->get_column('age') > $fourweeks ? 'older' : 'new');
+ # Fixed problems are either old or new
+ push @{$fixed->{$council}{$duration}}, $row if $row->state eq 'fixed';
+ # Open problems are either unknown, older, or new
+ push @{$open->{$council}{$type}}, $row if $row->state eq 'confirmed';
+}
+
+=head1 AUTHOR
+
+Matthew Somerville
+
+=head1 LICENSE
+
+Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
+Licensed under the Affero GPL.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
+# # RSS - reports for sent reports, area for all problems in area
+# if ($rss && $council) {
+# my $url = Page::short_name($council);
+# $url .= '/' . Page::short_name($ward) if $ward;
+# if ($rss eq 'area' && $area_type ne 'DIS' && $area_type ne 'CTY') {
+# # Two possibilites are the same for one-tier councils, so redirect one to the other
+# print $q->redirect($base_url . '/rss/reports/' . $url);
+# return;
+# }
+# my $type = 'council_problems'; # Problems sent to a council
+# my (@params, %title_params);
+# $title_params{COUNCIL} = $area_name;
+# push @params, $council->{id} if $rss eq 'reports';
+# push @params, $ward ? $ward->{id} : $council->{id};
+# if ($ward && $rss eq 'reports') {
+# $type = 'ward_problems'; # Problems sent to a council, restricted to a ward
+# $title_params{WARD} = $q_ward;
+# } elsif ($rss eq 'area') {
+# $title_params{NAME} = $ward ? $q_ward : $q_council;
+# $type = 'area_problems'; # Problems within an area
+# }
+# print $q->header( -type => 'application/xml; charset=utf-8' );
+# my $xsl = Cobrand::feed_xsl($cobrand);
+# my $out = FixMyStreet::Alert::generate_rss($type, $xsl, "/$url", \@params, \%title_params, $cobrand, $q);
+# $out =~ s/matthew.fixmystreet/emptyhomes.matthew.fixmystreet/g if $q->{site} eq 'emptyhomes';
+# print $out;
+# return;
+# }
+
diff --git a/perllib/FixMyStreet/Cobrand/Barnet.pm b/perllib/FixMyStreet/Cobrand/Barnet.pm
index 4d20d6522..eacb4ef70 100644
--- a/perllib/FixMyStreet/Cobrand/Barnet.pm
+++ b/perllib/FixMyStreet/Cobrand/Barnet.pm
@@ -9,7 +9,7 @@ use URI::Escape;
use mySociety::VotingArea;
sub site_restriction {
- return ( "and council='2489'", 'barnet' );
+ return ( "and council='2489'", 'barnet', { council => '2489' } );
}
sub base_url {
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
index e3c5ed821..7920a15d6 100644
--- a/perllib/FixMyStreet/Cobrand/Default.pm
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -6,6 +6,7 @@ use FixMyStreet;
use URI;
use Carp;
+use mySociety::MaPit;
=head2 new
@@ -111,7 +112,7 @@ empty string and site key 0 if the cobrand uses all the data.
=cut
-sub site_restriction { return ( "", 0 ) }
+sub site_restriction { return ( "", 0, {} ) }
=head2 contact_restriction
@@ -793,5 +794,24 @@ sub generate_problem_banner {
return $banner;
}
+
+sub reports_council_check {
+ my ( $self, $c, $code ) = @_;
+
+ if ($code =~ /^(\d\d)([a-z]{2})?([a-z]{2})?$/i) {
+ my $area = mySociety::MaPit::call( 'area', uc $code );
+ $c->detach( 'redirect_index' ) if $area->{error}; # Given a bad/old ONS code
+ if (length($code) == 6) {
+ my $council = mySociety::MaPit::call( 'area', $area->{parent_area} );
+ $c->stash->{ward} = $area;
+ $c->stash->{council} = $council;
+ $c->detach( 'redirect_ward' );
+ } else {
+ $c->stash->{council} = $area;
+ $c->detach( 'redirect_council' );
+ }
+ }
+}
+
1;
diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm
index 3981192c8..aaa5f281b 100644
--- a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm
+++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm
@@ -5,6 +5,7 @@ use strict;
use warnings;
use Carp;
+use mySociety::MaPit;
sub set_lang_and_domain {
my ( $self, $lang, $unicode, $dir ) = @_;
@@ -77,4 +78,36 @@ sub short_name {
}
+sub reports_council_check {
+ my ( $self, $c, $council ) = @_;
+
+ if ($council eq 'Oslo') {
+
+ # There are two Oslos (kommune and fylke), we only want one of them.
+ $c->stash->{council} = mySociety::MaPit::call('area', 3);
+ return 1;
+
+ } elsif ($council =~ /,/) {
+
+ # Some kommunes have the same name, use the fylke name to work out which.
+ my ($kommune, $fylke) = split /\s*,\s*/, $council;
+ my @area_types = $c->cobrand->area_types;
+ my $areas_k = mySociety::MaPit::call('areas', $kommune, type => \@area_types);
+ my $areas_f = mySociety::MaPit::call('areas', $fylke, type => \@area_types);
+ use Data::Dumper;
+ if (keys %$areas_f == 1) {
+ ($fylke) = values %$areas_f;
+ foreach (values %$areas_k) {
+ if ($_->{name} eq $kommune && $_->{parent_area} == $fylke->{id}) {
+ $c->stash->{council} = $_;
+ return 1;
+ }
+ }
+ }
+ # If we're here, we've been given a bad name.
+ $c->detach( 'redirect_index' );
+
+ }
+}
+
1;
diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm
index ca13f7130..57bbf5fb8 100644
--- a/perllib/FixMyStreet/DB/Result/Problem.pm
+++ b/perllib/FixMyStreet/DB/Result/Problem.pm
@@ -200,4 +200,29 @@ sub confirm {
return 1;
}
+=head2 councils
+
+Returns an array of councils to which a report was sent.
+
+=cut
+
+sub councils {
+ my $self = shift;
+ return () unless $self->council;
+ (my $council = $self->council) =~ s/\|.*$//;
+ my @council = split /,/, $council;
+ return @council;
+}
+
+=head2 url
+
+Returns a URL for this problem report.
+
+=cut
+
+sub url {
+ my $self = shift;
+ return "/report/" . $self->id;
+}
+
1;
diff --git a/perllib/Problems.pm b/perllib/Problems.pm
index 76ccdc677..48203b980 100644
--- a/perllib/Problems.pm
+++ b/perllib/Problems.pm
@@ -21,6 +21,11 @@ use mySociety::MaPit;
my $site_restriction = '';
my $site_key = 0;
+my $site_restriction_hash = {};
+
+sub site_restriction {
+ return $site_restriction_hash;
+}
sub set_site_restriction {
my $q = shift;
@@ -41,7 +46,7 @@ sub set_site_restriction_with_cobrand_object {
my $cobrand = shift;
my $cobrand_data = $cobrand->extra_data;
- ( $site_restriction, $site_key ) =
+ ( $site_restriction, $site_key, $site_restriction_hash ) =
$cobrand->site_restriction($cobrand_data);
}
diff --git a/templates/web/default/reports/council.html b/templates/web/default/reports/council.html
new file mode 100755
index 000000000..4067d0619
--- /dev/null
+++ b/templates/web/default/reports/council.html
@@ -0,0 +1,99 @@
+[% IF ward %]
+ [% name = "$ward.name, $council.name"
+ thing = loc('ward')
+ %]
+[% ELSE %]
+ [% name = council.name
+ thing = loc('council')
+ %]
+[% END %]
+
+[% INCLUDE 'header.html',
+ title = tprintf(loc('%s - Summary reports'), name)
+ context = 'reports'
+ rss = [ tprintf(loc('Problems within %s, FixMyStreet'), name), rss_url ]
+%]
+
+<p><a href="[% rss_url %]"><img align="right" src="/i/feed.png" width="16" height="16" title="[% loc('RSS feed') %]" alt="[% tprintf(loc('RSS feed of problems in this %s'), thing) %]" border="0" hspace="4"></a>
+
+[% IF c.cobrand.all_councils_report %]
+ [% tprintf( loc('This is a summary of all reports for one %s.'), thing ) %]
+[% ELSE %]
+ [% tprintf( loc('This is a summary of all reports for this %s.'), thing ) %]
+[% END %]
+
+[%# FIXME: It should link to council from a ward page, and should have list of wards on a council page. And a map?
+ The reason c.req.base/path is used below is that passing undef to uri_with
+ in a template actually passes "", and so the key still appears in the URL.
+%]
+
+[% IF c.req.parameters.all AND !c.cobrand.all_councils_report %]
+ [% tprintf( loc('You can <a href="%s">see less detail</a>.'), c.req.base _ c.req.path ) %]
+[% ELSIF !c.cobrand.all_councils_report %]
+ [% tprintf( loc('You can <a href="%s">see more details</a>.'), c.req.uri_with( { all = 1 } ) ) %]
+[% ELSIF c.req.parameters.all %]
+ [% tprintf( loc('You can <a href="%s">see less detail</a> or go back and <a href="/reports">show all councils</a>.'), c.req.base _ c.req.path ) %]
+[% ELSE %]
+ [% tprintf( loc('You can <a href="%s">see more details</a> or go back and <a href="/reports">show all councils</a>.'), c.req.uri_with( { all = 1 } ) ) %]
+[% END %]
+
+<h2>[% name %]</h2>
+
+<div id="col_problems">
+ [% INCLUDE column
+ title = loc('New problems')
+ problems = open.${council.id}.new
+ %]
+
+ [%# This doesn't really need a whole separate template %]
+ [% IF c.cobrand.moniker == 'emptyhomes' %]
+ [%
+ INCLUDE column
+ title = loc('Older problems')
+ problems = open.${council.id}.older.merge( open.${council.id}.unknown )
+ %]
+ [% ELSE %]
+ [% INCLUDE column
+ title = loc('Older problems')
+ problems = open.${council.id}.older
+ %]
+ [% INCLUDE column
+ title = loc('Old problems, state unknown')
+ problems = open.${council.id}.unknown
+ %]
+ [% END %]
+</div>
+
+<div id="col_fixed">
+ [% INCLUDE column
+ title = loc('Recently fixed')
+ problems = fixed.${council.id}.new
+ %]
+ [% INCLUDE column
+ title = loc('Old fixed')
+ problems = fixed.${council.id}.old
+ %]
+</div>
+
+[% INCLUDE 'footer.html' %]
+
+[% BLOCK column %]
+[% IF problems %]
+
+<h3>[% title %]</h3>
+
+<ul>
+[% FOREACH problem IN problems %]
+ <li><a href="[% c.uri_for(problem.url) %]">[% problem.title | html %]</a>
+ [% IF problem.councils.size > 1 %] <small>[% loc('(sent to both)') %]</small> [% END %]
+ [% IF c.cobrand.moniker != 'emptyhomes' %]
+ [% IF problem.councils.size == 0 %] <small>[% loc('(not sent to council)') %]</small> [% END %]
+ [% END %]
+ [% IF all %] <br><small>[% problem.detail %]</small> [% END %]
+ </li>
+[% END %]
+</ul>
+
+[% END %]
+[% END %]
+
diff --git a/templates/web/default/reports/index.html b/templates/web/default/reports/index.html
new file mode 100755
index 000000000..bb7824cad
--- /dev/null
+++ b/templates/web/default/reports/index.html
@@ -0,0 +1,35 @@
+[% INCLUDE 'header.html', title = loc('Summary reports') %]
+
+<p>
+[% loc('This is a summary of all reports on this site; select a particular council to see the reports sent there.') %]
+[% loc('Greyed-out lines are councils that no longer exist.') %]
+</p>
+
+<table cellpadding="3" cellspacing="1" border="0">
+<tr>
+<th>[% loc('Name') %]</th>
+<th>[% loc('New problems') %]</th>
+<th>[% loc('Older problems') %]</th>
+<th>[% loc('Old problems,<br>state unknown') %]</th>
+<th>[% loc('Recently fixed') %]</th>
+<th>[% loc('Older fixed') %]</th>
+</tr>
+
+[% FOREACH area IN areas_info_sorted %]
+<tr align="center"
+[%- IF area.generation_high == 10 %] class="gone"
+[%- ELSIF loop.count % 2 %] class="a"
+[%- END -%]
+>
+<td align="left"><a href="[% area.url %]">[% area.name %]</a></td>
+<td>[% open.${area.id}.new.size or 0 %]</td>
+<td>[% open.${area.id}.older.size or 0 %]</td>
+<td>[% open.${area.id}.unknown.size or 0 %]</td>
+<td>[% fixed.${area.id}.new.size or 0 %]</td>
+<td>[% fixed.${area.id}.old.size or 0 %]</td>
+</tr>
+[% END %]
+</table>
+
+[% INCLUDE 'footer.html' %]
+
diff --git a/templates/web/default/reports/ward.html b/templates/web/default/reports/ward.html
new file mode 100755
index 000000000..8b65ffb28
--- /dev/null
+++ b/templates/web/default/reports/ward.html
@@ -0,0 +1 @@
+[% INCLUDE reports/council.html %]
diff --git a/templates/web/emptyhomes/reports/index.html b/templates/web/emptyhomes/reports/index.html
new file mode 100755
index 000000000..13805ab43
--- /dev/null
+++ b/templates/web/emptyhomes/reports/index.html
@@ -0,0 +1,33 @@
+[% INCLUDE 'header.html', title = loc('Summary reports') %]
+
+<p>
+[% loc('This is a summary of all reports on this site; select a particular council to see the reports sent there.') %]
+[% loc('Greyed-out lines are councils that no longer exist.') %]
+</p>
+
+<table cellpadding="3" cellspacing="1" border="0">
+<tr>
+<th>[% loc('Name') %]</th>
+<th>[% loc('New problems') %]</th>
+<th>[% loc('Older problems') %]</th>
+<th>[% loc('Recently fixed') %]</th>
+<th>[% loc('Older fixed') %]</th>
+</tr>
+
+[% FOREACH area IN areas_info_sorted %]
+<tr align="center"
+[%- IF area.generation_high == 10 %] class="gone"
+[%- ELSIF loop.count % 2 %] class="a"
+[%- END -%]
+>
+<td align="left"><a href="[% area.url %]">[% area.name %]</a></td>
+<td>[% open.${area.id}.new.size or 0 %]</td>
+<td>[% open.${area.id}.older.list.size + open.${area.id}.unknown.list.size %]</td>
+<td>[% fixed.${area.id}.new.size or 0 %]</td>
+<td>[% fixed.${area.id}.old.size or 0 %]</td>
+</tr>
+[% END %]
+</table>
+
+[% INCLUDE 'footer.html' %]
+
diff --git a/templates/web/fiksgatami/reports/index.html b/templates/web/fiksgatami/reports/index.html
new file mode 100755
index 000000000..ab8ff6381
--- /dev/null
+++ b/templates/web/fiksgatami/reports/index.html
@@ -0,0 +1,34 @@
+[% INCLUDE 'header.html', title = loc('Summary reports') %]
+
+<p>
+[% loc('This is a summary of all reports on this site; select a particular council to see the reports sent there.') %]
+</p>
+
+<table cellpadding="3" cellspacing="1" border="0">
+<tr>
+<th>[% loc('Name') %]</th>
+<th>[% loc('New problems') %]</th>
+<th>[% loc('Older problems') %]</th>
+<th>[% loc('Old problems,<br>state unknown') %]</th>
+<th>[% loc('Recently fixed') %]</th>
+<th>[% loc('Older fixed') %]</th>
+</tr>
+
+[% FOREACH area IN areas_info_sorted %]
+[% NEXT IF area.id == 301 %]
+<tr align="center"
+[%- IF loop.count % 2 %] class="a"
+[%- END -%]
+>
+<td align="left"><a href="[% area.url %]">[% area.name %]</a></td>
+<td>[% open.${area.id}.new.size or 0 %]</td>
+<td>[% open.${area.id}.older.size or 0 %]</td>
+<td>[% open.${area.id}.unknown.size or 0 %]</td>
+<td>[% fixed.${area.id}.new.size or 0 %]</td>
+<td>[% fixed.${area.id}.old.size or 0 %]</td>
+</tr>
+[% END %]
+</table>
+
+[% INCLUDE 'footer.html' %]
+
diff --git a/templates/website/reports b/templates/website/reports
deleted file mode 100644
index 2519a0301..000000000
--- a/templates/website/reports
+++ /dev/null
@@ -1,12 +0,0 @@
-<p><a href="{{ $rss_url }}"><img align="right" src="/i/feed.png" width="16" height="16" title="{{ $rss_title }}" alt="{{ $rss_alt }}" border="0" hspace="4"></a> {{ $summary_title }} {{ $summary_line }}
-
-<h2>{{ $name }}</h2>
-
-<div id="col_problems">
-{{ $col_problems }}
-</div>
-
-<div id="col_fixed">
-{{ $col_fixed }}
-</div>
-
diff --git a/web/reports.cgi b/web/reports.cgi
deleted file mode 100755
index 22dbe344a..000000000
--- a/web/reports.cgi
+++ /dev/null
@@ -1,341 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# report.cgi:
-# Display summary reports for FixMyStreet
-# And RSS feeds for those reports etc.
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: reports.cgi,v 1.41 2009-12-08 11:13:30 louise Exp $
-
-use strict;
-use Standard;
-use Encode;
-use POSIX qw(strcoll);
-use URI::Escape;
-use FixMyStreet::Alert;
-use mySociety::MaPit;
-use mySociety::Web qw(ent NewURL);
-use mySociety::VotingArea;
-
-sub main {
- my $q = shift;
- my $all = $q->param('all') || 0;
- my $rss = $q->param('rss') || '';
- my $cobrand = Page::get_cobrand($q);
-
- # Look up council name, if given
- my $q_council = $q->param('council') || '';
- my $base_url = Cobrand::base_url($cobrand);
-
- # Manual misspelling redirect
- if ($q_council =~ /^rhondda cynon taff$/i) {
- print $q->redirect($base_url . '/reports/Rhondda+Cynon+Taf');
- return;
- }
-
- my ($one_council, $area_type, $area_name);
- if ($q_council =~ /^(\d\d)([a-z]{2})?([a-z]{2})?$/i) {
- my $va_info = mySociety::MaPit::call('area', uc $q_council);
- if ($va_info->{error}) { # Given a bad/old ONS code
- print $q->redirect($base_url . '/reports');
- return;
- }
- $area_name = Page::short_name($va_info);
- if (length($q_council) == 6) {
- $va_info = mySociety::MaPit::call('area', $va_info->{parent_area});
- $area_name = Page::short_name($va_info) . '/' . $area_name;
- }
- $rss = '/rss' if $rss;
- print $q->redirect($base_url . $rss . '/reports/' . $area_name);
- return;
- } elsif (mySociety::Config::get('COUNTRY') eq 'NO' && $q_council eq 'Oslo') {
- $one_council = mySociety::MaPit::call('area', 3);
- $area_type = $one_council->{type};
- $area_name = $one_council->{name};
- } elsif (mySociety::Config::get('COUNTRY') eq 'NO' && $q_council =~ /,/) {
- my ($kommune, $fylke) = split /\s*,\s*/, $q_council;
- my @area_types = Cobrand::area_types($cobrand);
- my $areas_k = mySociety::MaPit::call('areas', $kommune, type => \@area_types);
- my $areas_f = mySociety::MaPit::call('areas', $fylke, type => \@area_types);
- if (keys %$areas_f == 1) {
- ($fylke) = values %$areas_f;
- foreach (values %$areas_k) {
- if ($_->{name} eq $kommune && $_->{parent_area} == $fylke->{id}) {
- $one_council = $_;
- $area_type = $_->{type};
- $area_name = $_->{name};
- last;
- }
- }
- }
- if (!$one_council) { # Given a false council name
- print $q->redirect($base_url . '/reports');
- return;
- }
- } elsif ($q_council =~ /\D/) {
- my @area_types = Cobrand::area_types($cobrand);
- my $areas = mySociety::MaPit::call('areas', $q_council, type => \@area_types, min_generation=>Cobrand::area_min_generation($cobrand) );
- if (keys %$areas == 1) {
- ($one_council) = values %$areas;
- $area_type = $one_council->{type};
- $area_name = $one_council->{name};
- } else {
- foreach (keys %$areas) {
- if ($areas->{$_}->{name} eq $q_council || $areas->{$_}->{name} =~ /^\Q$q_council\E (Borough|City|District|County) Council$/) {
- $one_council = $areas->{$_};
- $area_type = $areas->{$_}->{type};
- $area_name = $q_council;
- }
- }
- }
- if (!$one_council) { # Given a false council name
- print $q->redirect($base_url . '/reports');
- return;
- }
- } elsif ($q_council =~ /^\d+$/) {
- my $va_info = mySociety::MaPit::call('area', $q_council);
- if ($va_info->{error}) {
- print $q->redirect($base_url . '/reports');
- return;
- }
- print $q->redirect($base_url . '/reports/' . Page::short_name($va_info));
- return;
- }
- $all = 0 unless $one_council;
-
- # Look up ward name, if given
- my $q_ward = $q->param('ward') || '';
- my $ward;
- if ($one_council && $q_ward) {
- my $qw = mySociety::MaPit::call('areas', $q_ward, type => $mySociety::VotingArea::council_child_types,
- min_generation => Cobrand::area_min_generation($cobrand));
- foreach my $id (sort keys %$qw) {
- if ($qw->{$id}->{parent_area} == $one_council->{id}) {
- $ward = $qw->{$id};
- last;
- }
- }
- if (!$ward) { # Given a false ward name
- print $q->redirect($base_url . '/reports/' . Page::short_name($one_council));
- return;
- }
- }
-
- # RSS - reports for sent reports, area for all problems in area
- if ($rss && $one_council) {
- my $url = Page::short_name($one_council);
- $url .= '/' . Page::short_name($ward) if $ward;
- if ($rss eq 'area' && $area_type ne 'DIS' && $area_type ne 'CTY') {
- # Two possibilites are the same for one-tier councils, so redirect one to the other
- print $q->redirect($base_url . '/rss/reports/' . $url);
- return;
- }
- my $type = 'council_problems'; # Problems sent to a council
- my (@params, %title_params);
- $title_params{COUNCIL} = $area_name;
- push @params, $one_council->{id} if $rss eq 'reports';
- push @params, $ward ? $ward->{id} : $one_council->{id};
- if ($ward && $rss eq 'reports') {
- $type = 'ward_problems'; # Problems sent to a council, restricted to a ward
- $title_params{WARD} = $q_ward;
- } elsif ($rss eq 'area') {
- $title_params{NAME} = $ward ? $q_ward : $q_council;
- $type = 'area_problems'; # Problems within an area
- }
- print $q->header( -type => 'application/xml; charset=utf-8' );
- my $xsl = Cobrand::feed_xsl($cobrand);
- my $out = FixMyStreet::Alert::generate_rss($type, $xsl, "/$url", \@params, \%title_params, $cobrand, $q);
- $out =~ s/matthew.fixmystreet/emptyhomes.matthew.fixmystreet/g if $q->{site} eq 'emptyhomes';
- print $out;
- return;
- }
-
- my $areas_info;
- if ($one_council) {
- $areas_info = mySociety::MaPit::call('areas', [ $one_council->{id}, $one_council->{parent_area} ])
- if $one_council->{parent_area};
- $areas_info = { $one_council->{id} => $one_council }
- unless $areas_info;
- } else {
- # Show all councils on main report page
- my @area_types = Cobrand::area_types($cobrand);
- $areas_info = mySociety::MaPit::call('areas', \@area_types, min_generation=>Cobrand::area_min_generation($cobrand) );
- }
-
- my $problems = Problems::council_problems(
- $ward ? $ward->{id} : undef,
- $one_council ? $one_council->{id} : undef
- );
-
- my (%fixed, %open);
- my $re_councils = join('|', keys %$areas_info);
- foreach my $row (@$problems) {
- if (!$row->{council}) {
- # Problem was not sent to any council, add to possible councils
- while ($row->{areas} =~ /,($re_councils)(?=,)/g) {
- add_row($row, 0, $1, \%fixed, \%open);
- }
- } else {
- # Add to councils it was sent to
- $row->{council} =~ s/\|.*$//;
- my @council = split /,/, $row->{council};
- foreach (@council) {
- next if $one_council && $_ != $one_council->{id};
- add_row($row, scalar @council, $_, \%fixed, \%open);
- }
- }
- }
-
- if (!$one_council) {
- print Page::header($q, title=>_('Summary reports'), expires=>'+1h');
- print $q->p(
- _('This is a summary of all reports on this site; select a particular council to see the reports sent there.'), ' ',
- _('Greyed-out lines are councils that no longer exist.')
- );
- my $c = 0;
- print '<table cellpadding="3" cellspacing="1" border="0">';
- print '<tr><th>' . _('Name') . '</th><th>' . _('New problems') . '</th><th>' . _('Older problems') . '</th>';
- if ($q->{site} ne 'emptyhomes') {
- print '<th>' . _('Old problems,<br>state unknown') . '</th>';
- }
- print '<th>' . _('Recently fixed') . '</th><th>' . _('Older fixed') . '</th></tr>';
- foreach (sort { strcoll($areas_info->{$a}->{name}, $areas_info->{$b}->{name}) } keys %$areas_info) {
- next if mySociety::Config::get('COUNTRY') eq 'NO' && $_ eq 301; # Only want one Oslo
- print '<tr align="center"';
- ++$c;
- if (mySociety::Config::get('COUNTRY') eq 'GB' && $areas_info->{$_}->{generation_high} == 10) {
- print ' class="gone"';
- } elsif ($c%2) {
- print ' class="a"';
- }
- my $url = Page::short_name($areas_info->{$_}, $areas_info);
- my $cobrand_url = Cobrand::url($cobrand, "/reports/$url", $q);
- print '><td align="left"><a href="' . $cobrand_url . '">' .
- $areas_info->{$_}->{name};
- if ($areas_info->{$_}->{parent_area} && $url =~ /,|%2C/) {
- print ', ' . $areas_info->{$areas_info->{$_}->{parent_area}}->{name};
- }
- print '</a></td>';
- summary_cell(\@{$open{$_}{new}});
- if ($q->{site} eq 'emptyhomes') {
- my $c = 0;
- $c += @{$open{$_}{older}} if $open{$_}{older};
- $c += @{$open{$_}{unknown}} if $open{$_}{unknown};
- summary_cell($c);
- } else {
- summary_cell(\@{$open{$_}{older}});
- summary_cell(\@{$open{$_}{unknown}});
- }
- summary_cell(\@{$fixed{$_}{new}});
- summary_cell(\@{$fixed{$_}{old}});
- print "</tr>\n";
- }
- print '</table>';
- } else {
- my $name = $one_council->{name};
- if (!$name) {
- print Page::header($q, title=>_("Summary reports"));
- print "Council with identifier " . ent($one_council->{id}). " not found. ";
- print $q->a({href => Cobrand::url($cobrand, '/reports', $q) }, 'Show all councils');
- print ".";
- } else {
- my $rss_url = '/rss/reports/' . Page::short_name($one_council, $areas_info);
- my $thing = _('council');
- if ($ward) {
- $rss_url .= '/' . Page::short_name($ward);
- $thing = 'ward';
- $name = ent($q_ward) . ", $name";
- }
- my $all_councils_report = Cobrand::all_councils_report($cobrand);
-
- my %vars = (
- rss_title => _('RSS feed'),
- rss_alt => sprintf(_('RSS feed of problems in this %s'), $thing),
- rss_url => Cobrand::url($cobrand, $rss_url, $q),
- url_home => Cobrand::url($cobrand, '/', $q),
- summary_title => $all_councils_report
- ? sprintf(_('This is a summary of all reports for one %s.'), $thing)
- : sprintf(_('This is a summary of all reports for this %s.'), $thing),
- name => $name,
- );
- if ($all && ! $all_councils_report) {
- $vars{summary_line} = sprintf(_('You can <a href="%s">see less detail</a>.'), Cobrand::url($cobrand, NewURL($q), $q));
- } elsif (! $all_councils_report) {
- $vars{summary_line} = sprintf(_('You can <a href="%s">see more details</a>.'), Cobrand::url($cobrand, NewURL($q, all=>1), $q));
- } elsif ($all) {
- $vars{summary_line} = sprintf(_('You can <a href="%s">see less detail</a> or go back and <a href="/reports">show all councils</a>.'), Cobrand::url($cobrand, NewURL($q), $q));
- } else {
- $vars{summary_line} = sprintf(_('You can <a href="%s">see more details</a> or go back and <a href="/reports">show all councils</a>.'), Cobrand::url($cobrand, NewURL($q, all=>1), $q));
- }
-
- my $id = $one_council->{id};
- if ($open{$id}) {
- my $col = list_problems($q, _('New problems'), $open{$id}{new}, $all, 0);
- my $old = [];
- if ($q->{site} eq 'emptyhomes') {
- push @$old, @{$open{$id}{older}} if $open{$id}{older};
- push @$old, @{$open{$id}{unknown}} if $open{$id}{unknown};
- } else {
- $old = $open{$id}{older};
- }
- $col .= list_problems($q, _('Older problems'), $old, $all, 0);
- if ($q->{site} ne 'emptyhomes') {
- $col .= list_problems($q, _('Old problems, state unknown'), $open{$id}{unknown}, $all, 0);
- }
- $vars{col_problems} = $col;
- }
- if ($fixed{$id}) {
- my $col = list_problems($q, _('Recently fixed'), $fixed{$id}{new}, $all, 1);
- $col .= list_problems($q, _('Old fixed'), $fixed{$id}{old}, $all, 1);
- $vars{col_fixed} = $col;
- }
- print Page::header($q, context => 'reports', title=>sprintf(_('%s - Summary reports'), $name), rss => [ sprintf(_('Problems within %s, FixMyStreet'), $name), Cobrand::url($cobrand, $rss_url, $q) ]);
- print Page::template_include('reports', $q, Page::template_root($q), %vars);
- }
- }
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub add_row {
- my ($row, $councils, $council, $fixed, $open) = @_;
- my $fourweeks = 4*7*24*60*60;
- my $duration = ($row->{duration} > 2 * $fourweeks) ? 'old' : 'new';
- my $type = ($row->{duration} > 2 * $fourweeks)
- ? 'unknown'
- : ($row->{age} > $fourweeks ? 'older' : 'new');
- $row->{councils} = $councils;
- #Fixed problems are either old or new
- push @{$fixed->{$council}{$duration}}, $row if $row->{state} eq 'fixed';
- # Open problems are either unknown, older, or new
- push @{$open->{$council}{$type}}, $row if $row->{state} eq 'confirmed';
-}
-
-sub summary_cell {
- my $c = shift;
- $c = 0 unless defined $c;
- $c = @$c if ref($c) eq 'ARRAY';
- print '<td>' . $c . '</td>';
-}
-
-sub list_problems {
- my ($q, $title, $problems, $all, $fixed) = @_;
- return '' unless $problems;
- my $cobrand = Page::get_cobrand($q);
- my $out = "<h3>$title</h3>\n<ul>";
- foreach (sort { $fixed ? ($a->{duration} <=> $b->{duration}) : ($a->{age} <=> $b->{age}) } @$problems) {
- my $url = Cobrand::url($cobrand, "/report/" . $_->{id}, $q);
- $out .= '<li><a href="' . $url . '">';
- $out .= ent($_->{title});
- $out .= '</a>';
- $out .= ' <small>' . _('(sent to both)') . '</small>' if $_->{councils}>1;
- $out .= ' <small>' . _('(not sent to council)') . '</small>' if $_->{councils}==0 && $q->{site} ne 'emptyhomes';
- $out .= '<br><small>' . ent($_->{detail}) . '</small>' if $all;
- $out .= '</li>';
- }
- $out .= '</ul>';
- return $out;
-}
-