#!/usr/bin/perl -w -I../perllib
# index.cgi:
# Main code for FixMyStreet
#
# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
#
# $Id: index.cgi,v 1.192 2008-05-06 10:06:13 matthew Exp $
use strict;
use Standard;
use Error qw(:try);
use File::Slurp;
use LWP::Simple;
use RABX;
use CGI::Carp;
use URI::Escape;
use mySociety::AuthToken;
use mySociety::Config;
use mySociety::DBHandle qw(select_all);
use mySociety::EmailUtil;
use mySociety::Gaze;
use mySociety::GeoUtil;
use mySociety::MaPit;
use mySociety::PostcodeUtil;
use mySociety::Random;
use mySociety::VotingArea;
use mySociety::Web qw(ent NewURL);
BEGIN {
if (!dbh()->selectrow_array('select secret from secret for update of secret')) {
local dbh()->{HandleError};
dbh()->do('insert into secret (secret) values (?)', {}, unpack('h*', mySociety::Random::random_bytes(32)));
}
dbh()->commit();
}
# Main code for index.cgi
sub main {
my $q = shift;
my $out = '';
my %params;
if ($q->param('submit_problem') || ($q->param('submit_map') && $q->param('submit_map')==2)) {
$params{title} = _('Submitting your report');
($out) = submit_problem($q);
} elsif ($q->param('submit_update')) {
$params{title} = _('Submitting your update');
($out) = submit_update($q);
} elsif ($q->param('submit_map')) {
($out, %params) = display_form($q);
$params{title} = _('Reporting a problem');
} elsif ($q->param('id')) {
($out, %params) = display_problem($q);
$params{title} .= ' - ' . _('Viewing a problem');
} elsif ($q->param('pc') || ($q->param('x') && $q->param('y'))) {
($out, %params) = display_location($q);
$params{title} = _('Viewing a location');
} else {
$out = front_page($q);
}
print Page::header($q, %params);
print $out;
my %footerparams;
$footerparams{js} = $params{js} if $params{js};
print Page::footer($q, %footerparams);
}
Page::do_fastcgi(\&main);
# Display front page
sub front_page {
my ($q, $error) = @_;
my $pc_h = ent($q->param('pc') || '');
my $out = '
' . _('Report, view, or discuss local problems') . ' ' . _('(like graffiti, fly tipping, broken paving slabs, or street lighting)') . '
';
$out .= '
' . $error . '
' if ($error);
my $fixed = dbh()->selectrow_array("select count(*) from problem where state='fixed' and lastupdate>ms_current_timestamp()-'1 month'::interval");
my $updates = dbh()->selectrow_array("select count(*) from comment where state='confirmed'");
my $new = dbh()->selectrow_array("select count(*) from problem where state in ('confirmed','fixed') and confirmed>ms_current_timestamp()-'1 week'::interval");
my $new_text = 'in past week';
if ($new > $fixed) {
$new = dbh()->selectrow_array("select count(*) from problem where state in ('confirmed','fixed') and confirmed>ms_current_timestamp()-'3 days'::interval");
$new_text = 'recently';
}
$out .= '
EOF
$out .= $q->h2(_('How to report a problem'));
$out .= $q->ol(
$q->li(_('Enter a nearby UK postcode, or street name and area')),
$q->li(_('Locate the problem on a map of the area')),
$q->li(_('Enter details of the problem')),
$q->li(_('We send it to the council on your behalf'))
);
$out .= $q->h2(_('FixMyStreet updates'));
$out .= <
$new reports $new_text
$fixed fixed in past month
$updates updates on reports
EOF
my $recent_photos = Page::recent_photos(3);
$out .= $q->h2(_('Photos of recent reports')) . $recent_photos if $recent_photos;
my $probs = select_all("select id,title from problem
where state in ('confirmed', 'fixed')
order by confirmed desc limit 5");
$out .= $q->h2(_('Recently reported problems')) . '
';
return $out;
}
sub submit_update {
my $q = shift;
my @vars = qw(id name email update fixed upload_fileid);
my %input = map { $_ => $q->param($_) || '' } @vars;
my @errors;
my $fh = $q->upload('photo');
if ($fh) {
my $err = Page::check_photo($q, $fh);
push @errors, $err if $err;
}
push(@errors, 'Please enter a message') unless $input{update} =~ /\S/;
$input{name} = undef unless $input{name} =~ /\S/;
if ($input{email} !~ /\S/) {
push(@errors, 'Please enter your email');
} elsif (!mySociety::EmailUtil::is_valid_email($input{email})) {
push(@errors, 'Please enter a valid email');
}
my $image;
if ($fh) {
try {
$image = Page::process_photo($fh);
} catch Error::Simple with {
my $e = shift;
push(@errors, sprintf(_("That image doesn't appear to have uploaded correctly (%s), please try again."), $e));
};
}
if ($input{upload_fileid}) {
open FP, mySociety::Config::get('UPLOAD_CACHE') . $input{upload_fileid};
$image = join('', );
close FP;
}
return display_problem($q, @errors) if (@errors);
my $id = dbh()->selectrow_array("select nextval('comment_id_seq');");
Page::workaround_pg_bytea("insert into comment
(id, problem_id, name, email, website, text, state, mark_fixed, photo)
values (?, ?, ?, ?, '', ?, 'unconfirmed', ?, ?)", 7,
$id, $input{id}, $input{name}, $input{email}, $input{update},
$input{fixed} ? 't' : 'f', $image);
my %h = ();
$h{update} = $input{update};
$h{name} = $input{name} ? $input{name} : _("Anonymous");
$h{url} = mySociety::Config::get('BASE_URL') . '/C/' . mySociety::AuthToken::store('update', $id);
dbh()->commit();
my $out = Page::send_email($input{email}, $input{name}, 'update', %h);
return $out;
}
sub submit_problem {
my $q = shift;
my @vars = qw(council title detail name email phone pc easting northing skipped anonymous category flickr upload_fileid);
my %input = map { $_ => scalar $q->param($_) } @vars;
for (qw(title detail)) {
$input{$_} = lc $input{$_} if $input{$_} !~ /[a-z]/;
$input{$_} = ucfirst $input{$_};
$input{$_} =~ s/\b(dog\s*)shit\b/$1poo/ig;
}
my @errors;
my $fh = $q->upload('photo');
if ($fh) {
my $err = Page::check_photo($q, $fh);
push @errors, $err if $err;
}
push(@errors, _('No council selected')) unless ($input{council} && $input{council} =~ /^(?:-1|[\d,]+(?:\|[\d,]+)?)$/);
push(@errors, _('Please enter a subject')) unless $input{title} =~ /\S/;
push(@errors, _('Please enter some details')) unless $input{detail} =~ /\S/;
if ($input{name} !~ /\S/) {
push @errors, _('Please enter your name');
} elsif (length($input{name}) < 5 || $input{name} !~ /\s/ || $input{name} =~ /\ba\s*n+on+((y|o)mo?u?s)?(ly)?\b/i) {
push @errors, _('Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box');
}
if ($input{email} !~ /\S/) {
push(@errors, _('Please enter your email'));
} elsif (!mySociety::EmailUtil::is_valid_email($input{email})) {
push(@errors, _('Please enter a valid email'));
}
if ($input{category} && $input{category} eq '-- Pick a category --') {
push (@errors, _('Please choose a category'));
$input{category} = '';
}
return display_form($q, @errors) if (@errors); # Short circuit
my $areas;
if ($input{easting} && $input{northing}) {
$areas = mySociety::MaPit::get_voting_areas_by_location(
{ easting=>$input{easting}, northing=>$input{northing} },
'polygon', [qw(WMC CTY CED DIS DIW MTD MTW COI COP LGD LGE UTA UTE UTW LBO LBW LAC SPC WAC NIE)]
);
if ($input{council} =~ /^[\d,]+(\|[\d,]+)?$/) {
my $no_details = $1 || '';
my %va = map { $_ => 1 } @$mySociety::VotingArea::council_parent_types;
my %councils;
foreach (keys %$areas) {
$councils{$_} = 1 if $va{$areas->{$_}};
}
my @input_councils = split /,|\|/, $input{council};
foreach (@input_councils) {
if (!$councils{$_}) {
push(@errors, _('That location is not part of that council'));
last;
}
}
if ($no_details) {
$input{council} =~ s/\Q$no_details\E//;
@input_councils = split /,/, $input{council};
}
# Check category here, won't be present if council is -1
my @valid_councils = @input_councils;
if ($input{category}) {
my $categories = select_all("select area_id from contacts
where deleted='f' and area_id in ("
. $input{council} . ') and category = ?', $input{category});
push (@errors, 'Please choose a category') unless @$categories;
@valid_councils = map { $_->{area_id} } @$categories;
foreach my $c (@valid_councils) {
if ($no_details =~ /$c/) {
push(@errors, _('We have details for that council'));
$no_details =~ s/,?$c//;
}
}
}
$input{council} = join(',', @valid_councils) . $no_details;
}
$areas = ',' . join(',', sort keys %$areas) . ',';
} elsif ($input{easting} || $input{northing}) {
push(@errors, _('Somehow, you only have one co-ordinate. Please try again.'));
} else {
push(@errors, _('You haven\'t specified any sort of co-ordinates. Please try again.'));
}
my $image;
if ($fh) {
try {
$image = Page::process_photo($fh);
} catch Error::Simple with {
my $e = shift;
push(@errors, sprintf(_("That image doesn't appear to have uploaded correctly (%s), please try again."), $e));
};
}
if ($input{upload_fileid}) {
open FP, mySociety::Config::get('UPLOAD_CACHE') . $input{upload_fileid};
$image = join('', );
close FP;
}
return display_form($q, @errors) if (@errors);
delete $input{council} if $input{council} eq '-1';
my $used_map = $input{skipped} ? 'f' : 't';
$input{category} = _('Other') unless $input{category};
my ($id, $out);
if (my $token = $input{flickr}) {
my $id = mySociety::AuthToken::retrieve('flickr', $token);
if ($id) {
dbh()->do("update problem set postcode=?, easting=?, northing=?, title=?, detail=?,
name=?, email=?, phone=?, state='confirmed', council=?, used_map='t',
anonymous=?, category=?, confirmed=ms_current_timestamp(),
lastupdate=ms_current_timestamp() where id=?", {}, $input{pc}, $input{easting}, $input{northing},
$input{title}, $input{detail}, $input{name}, $input{email},
$input{phone}, $input{council}, $input{anonymous} ? 'f' : 't',
$input{category}, $id);
dbh()->commit();
$out = $q->p(sprintf(_('You have successfully confirmed your report and you can now view it on the site.'), "/?id=$id"));
} else {
$out = $q->p(_('There appears to have been a problem.'));
}
} else {
$id = dbh()->selectrow_array("select nextval('problem_id_seq');");
Page::workaround_pg_bytea("insert into problem
(id, postcode, easting, northing, title, detail, name,
email, phone, photo, state, council, used_map, anonymous, category, areas)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'unconfirmed', ?, ?, ?, ?, ?)", 10,
$id, $input{pc}, $input{easting}, $input{northing}, $input{title},
$input{detail}, $input{name}, $input{email}, $input{phone}, $image,
$input{council}, $used_map, $input{anonymous} ? 'f': 't', $input{category},
$areas);
my %h = ();
$h{title} = $input{title};
$h{detail} = $input{detail};
$h{name} = $input{name};
$h{url} = mySociety::Config::get('BASE_URL') . '/P/' . mySociety::AuthToken::store('problem', $id);
dbh()->commit();
$out = Page::send_email($input{email}, $input{name}, 'problem', %h);
}
return $out;
}
sub display_form {
my ($q, @errors) = @_;
my ($pin_x, $pin_y, $pin_tile_x, $pin_tile_y) = (0,0,0,0);
my @vars = qw(title detail name email phone pc easting northing x y skipped council anonymous flickr upload_fileid);
my %input = map { $_ => $q->param($_) || '' } @vars;
my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
my @ps = $q->param;
foreach (@ps) {
($pin_tile_x, $pin_tile_y, $pin_x) = ($1, $2, $q->param($_)) if /^tile_(\d+)\.(\d+)\.x$/;
$pin_y = $q->param($_) if /\.y$/;
}
return display_location($q)
unless ($pin_x && $pin_y)
|| ($input{easting} && $input{northing})
|| ($input{skipped} && $input{x} && $input{y})
|| ($input{skipped} && $input{pc})
|| ($input{flickr} && $input{pc});
my $out = '';
my ($px, $py, $easting, $northing);
if ($input{skipped}) {
# Map is being skipped
if ($input{x} && $input{y}) {
$easting = Page::tile_to_os($input{x});
$northing = Page::tile_to_os($input{y});
} else {
my ($x, $y, $e, $n, $error) = Page::geocode($input{pc});
$easting = $e; $northing = $n;
}
} elsif ($pin_x && $pin_y) {
# Map was clicked on
$pin_x = Page::click_to_tile($pin_tile_x, $pin_x);
$pin_y = Page::click_to_tile($pin_tile_y, $pin_y, 1);
$input{x} ||= int($pin_x) - 1;
$input{y} ||= int($pin_y) - 1;
$px = Page::tile_to_px($pin_x, $input{x});
$py = Page::tile_to_px($pin_y, $input{y}, 1);
$easting = Page::tile_to_os($pin_x);
$northing = Page::tile_to_os($pin_y);
} elsif ($input{flickr} && $input{pc} && !$input{easting} && !$input{northing}) {
my ($x, $y, $e, $n, $error) = Page::geocode($input{pc});
$easting = $e; $northing = $n;
$input{x} = int(Page::os_to_tile($easting));
$input{y} = int(Page::os_to_tile($northing));
$px = Page::os_to_px($easting, $input{x});
$py = Page::os_to_px($northing, $input{y}, 1);
} else {
# Normal form submission
my ($x, $y, $tile_x, $tile_y);
($x, $y, $tile_x, $tile_y, $px, $py) = Page::os_to_px_with_adjust($q, $input{easting}, $input{northing}, undef, undef);
$input{x} = $tile_x;
$input{y} = $tile_y;
$easting = $input_h{easting};
$northing = $input_h{northing};
}
my $all_councils = mySociety::MaPit::get_voting_areas_by_location(
{ easting => $easting, northing => $northing },
'polygon', $mySociety::VotingArea::council_parent_types);
if ($q->{site} eq 'scambs') {
#delete $all_councils->{2218};
#@categories = ('Abandoned vehicles', 'Discarded hypodermic needles',
#'Dog fouling', 'Flytipping', 'Graffiti', 'Lighting (e.g. security lights)',
#'Litter', 'Neighbourhood noise');
}
$all_councils = [ keys %$all_councils ];
return display_location($q, _('That spot does not appear to be covered by a council - if it is past the shoreline, for example, please specify the closest point on land.')) unless @$all_councils;
my $areas_info = mySociety::MaPit::get_voting_areas_info($all_councils);
# Look up categories for this council or councils
my $category = '';
my %council_ok;
if ($q->{site} ne 'emptyhomes') { # No category
my $categories = select_all("select area_id, category from contacts
where deleted='f' and area_id in (" . join(',', @$all_councils) . ')');
@$categories = sort { $a->{category} cmp $b->{category} } @$categories;
my @categories;
foreach (@$categories) {
$council_ok{$_->{area_id}} = 1;
next if $_->{category} eq _('Other');
push @categories, $_->{category};
}
if (@categories) {
@categories = ('-- Pick a category --', @categories, _('Other'));
$category = $q->div($q->label({'for'=>'form_category'}, _('Category:')),
$q->popup_menu(-name=>'category', -values=>\@categories,
-attributes=>{id=>'form_category'})
);
}
}
my @councils = keys %council_ok;
my $details;
if (@councils == @$all_councils) {
$details = 'all';
} elsif (@councils == 0) {
$details = 'none';
} else {
$details = 'some';
}
if ($input{skipped}) {
$out .= <
EOF
$out .= $q->h1(_('Reporting a problem')) . '
';
} else {
my $pins = Page::display_pin($q, $px, $py, 'purple');
$out .= Page::display_map($q, x => $input{x}, y => $input{y}, type => 2,
pins => $pins, px => $px, py => $py );
$out .= $q->h1(_('Reporting a problem')) . ' ';
$out .= $q->p(_('You have located the problem at the point marked with a purple pin on the map.
If this is not the correct location, simply click on the map again. '));
}
if ($q->{site} ne 'emptyhomes') { # No "send to council" blurb
if ($details eq 'all') {
$out .= '
All the information you provide here will be sent to '
. join(' or ', map { $areas_info->{$_}->{name} } @$all_councils)
. '. On the site, we will show the subject and details of the problem,
plus your name if you give us permission.';
$out .= '';
} elsif ($details eq 'some') {
my $e = mySociety::Config::get('CONTACT_EMAIL');
my %councils = map { $_ => 1 } @councils;
my @missing;
foreach (@$all_councils) {
push @missing, $_ unless $councils{$_};
}
my $n = @missing;
my $list = join(' or ', map { $areas_info->{$_}->{name} } @missing);
$out .= '
All the information you provide here will be sent to '
. join(' or ', map { $areas_info->{$_}->{name} } @councils)
. '. On the site, we will show the subject and details of the problem,
plus your name if you give us permission.';
$out .= ' We do not yet have details for the other council';
$out .= ($n>1) ? 's that cover' : ' that covers';
$out .= " this location. You can help us by finding a contact email address for local
problems for $list and emailing it to us at $e.";
$out .= '';
} else {
my $e = mySociety::Config::get('CONTACT_EMAIL');
my $list = join(' or ', map { $areas_info->{$_}->{name} } @$all_councils);
my $n = @$all_councils;
$out .= '
We do not yet have details for the council';
$out .= ($n>1) ? 's that cover' : ' that covers';
$out .= " this location. If you submit a problem here it will be
left on the site, but not reported to the council.
You can help us by finding a contact email address for local
problems for $list and emailing it to us at $e.";
$out .= '';
}
}
if ($input{skipped}) {
$out .= $q->p(_('Please fill in the form below with details of the problem,
and describe the location as precisely as possible in the details box.'));
} elsif ($details ne 'none') {
$out .= '
Please fill in details of the problem below. The council won\'t be able
to help unless you leave as much detail as you can, so please describe the exact location of
the problem (e.g. on a wall), what it is, how long it has been there, a description (and a
photo of the problem if you have one), etc.';
} else {
$out .= $q->p(_('Please fill in details of the problem below.'));
}
$out .= '
';
if (@errors) {
$out .= '
EOF
$out .= <
EOF
if (my $token = $input{flickr}) {
my $id = mySociety::AuthToken::retrieve('flickr', $token);
if ($id) {
$out .= '
The photo you uploaded was:
';
$out .= '
';
}
} else {
$out .= <
EOF
}
$out .= <
(we never show your email address or phone number)
(optional)
EOF
$out .= <{site} eq 'emptyhomes'; # No notes
Please note:
Please be polite, concise and to the point.
Please do not be abusive — abusing your council devalues the service for all users.
Writing your message entirely in block capitals makes it hard to read,
as does a lack of punctuation.
Remember that FixMyStreet is primarily for reporting physical
problems that can be fixed. If your problem is not appropriate for
submission via this site remember that you can contact your council
directly using their own website.
EOF
$out .= <
EOF
$out .= Page::display_map_end(1);
my %params = (
js => <
swfu = new SWFUpload(swfu_settings);
EOF
);
return ($out, %params);
}
sub display_location {
my ($q, @errors) = @_;
my @vars = qw(pc x y);
my %input = map { $_ => $q->param($_) || '' } @vars;
my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
my($error, $easting, $northing);
my $x = $input{x}; my $y = $input{y};
$x ||= 0; $x += 0;
$y ||= 0; $y += 0;
return front_page($q, @errors) unless $x || $y || $input{pc};
if (!$x && !$y) {
try {
($x, $y, $easting, $northing, $error) = Page::geocode($input{pc});
} catch Error::Simple with {
$error = shift;
};
}
return Page::geocode_choice($error, '/') if (ref($error) eq 'ARRAY');
return front_page($q, $error) if ($error);
my ($pins, $current_map, $current, $fixed, $dist) = map_pins($q, $x, $y);
my $out = Page::display_map($q, x => $x, y => $y, type => 1, pins => $pins );
$out .= $q->h1(_('Problems in this area'));
if (@errors) {
$out .= '
' . join('
', @errors) . '
';
}
my $skipurl = NewURL($q, 'submit_map'=>1, skipped=>1);
$out .= $q->p({-id=>'text_map'}, _('To report a problem, simply click on the map at the correct location.'));
$out .= $q->p({-id=>'text_no_map'}, sprintf(_("If you cannot see a map – if you have images turned off,
or are using a text only browser, for example – and you
wish to report a problem, please
skip this step and we will ask you
to describe the location of the problem instead."), $skipurl));
$out .= '
h2(_('Recent problems reported near here'));
my $list = '';
foreach (@$current_map) {
$list .= '
';
}
if (@$fixed) {
$out .= "$list\n";
} else {
$out .= $q->p(_('No problems have been fixed yet'));
}
$out .= '';
$out .= Page::display_map_end(1);
my %params = (
rss => [ _('Recent local problems, FixMyStreet'), "/rss/$x,$y" ]
);
return ($out, %params);
}
sub display_problem {
my ($q, @errors) = @_;
my @vars = qw(id name email update fixed upload_fileid x y);
my %input = map { $_ => $q->param($_) || '' } @vars;
my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
$input{x} ||= 0; $input{x} += 0;
$input{y} ||= 0; $input{y} += 0;
# Get all information from database
return display_location($q, 'Unknown problem ID') if $input{id} =~ /\D/;
my $problem = dbh()->selectrow_hashref(
"select id, easting, northing, council, category, title, detail, (photo is not null) as photo,
used_map, name, anonymous, extract(epoch from confirmed) as time,
state, extract(epoch from whensent-confirmed) as whensent,
extract(epoch from ms_current_timestamp()-lastupdate) as duration
from problem where id=? and state in ('confirmed','fixed', 'hidden')", {}, $input{id});
return display_location($q, 'Unknown problem ID') unless $problem;
return front_page($q, 'That problem has been hidden from public view as it contained inappropriate public details') if $problem->{state} eq 'hidden';
my ($x, $y, $x_tile, $y_tile, $px, $py) = Page::os_to_px_with_adjust($q, $problem->{easting}, $problem->{northing}, $input{x}, $input{y});
my $out = '';
my $pins = Page::display_pin($q, $px, $py, 'blue');
$out .= Page::display_map($q, x => $x_tile, y => $y_tile, type => 0,
pins => $pins, px => $px, py => $py );
if ($problem->{state} eq 'confirmed' && $problem->{duration} > 8*7*24*60*60) {
$out .= $q->p({id => 'unknown'}, _('This problem is old and of unknown status.'))
}
if ($problem->{state} eq 'fixed') {
$out .= $q->p({id => 'fixed'}, _('This problem has been fixed.'))
}
$out .= Page::display_problem_text($q, $problem);
$out .= $q->p({align=>'right'},
$q->small($q->a({href => '/contact?id=' . $input{id}}, 'Offensive? Unsuitable? Tell us'))
);
# Try and have pin near centre of map
$x_tile -= 1 if $x - $x_tile < 0.5;
$y_tile -= 1 if $y - $y_tile < 0.5;
my $back = NewURL($q, id=>undef, x=>$x_tile, y=>$y_tile, submit_update=>undef);
$out .= '
Receive email when updates are left on this problem
EOF
$out .= ' ';
$out .= '
';
$out .= Page::display_problem_updates($input{id});
$out .= $q->h2(_('Provide an update'));
$out .= $q->p($q->small(_('Please note that updates are not sent to the council.')))
unless $q->{site} eq 'emptyhomes'; # No council blurb
if (@errors) {
$out .= '
EOF
$out .= Page::display_map_end(0);
my $js = <
swfu = new SWFUpload(swfu_settings);
EOF
my %params = (
rss => [ _('Updates to this problem, FixMyStreet'), "/rss/$input_h{id}" ],
js => $js,
title => $problem->{title}
);
return ($out, %params);
}
sub map_pins {
my ($q, $x, $y) = @_;
my $pins = '';
my $min_e = Page::tile_to_os($x-1);
my $min_n = Page::tile_to_os($y-1);
my $mid_e = Page::tile_to_os($x+1);
my $mid_n = Page::tile_to_os($y+1);
my $max_e = Page::tile_to_os($x+3);
my $max_n = Page::tile_to_os($y+3);
my $current_map = select_all(
"select id,title,easting,northing from problem where state='confirmed'
and easting>=? and easting and northing>=? and northing
order by created desc limit 9", $min_e, $max_e, $min_n, $max_n);
my @ids = ();
my $count_prob = 1;
my $count_fixed = 1;
foreach (@$current_map) {
push(@ids, $_->{id});
my $px = Page::os_to_px($_->{easting}, $x);
my $py = Page::os_to_px($_->{northing}, $y, 1);
$pins .= Page::display_pin($q, $px, $py, 'red', $count_prob++);
}
my ($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($mid_e, $mid_n, 'G');
my $dist = mySociety::Gaze::get_radius_containing_population($lat, $lon, 200000);
$dist = int($dist*10+0.5)/10;
# XXX: Change to only show problems with extract(epoch from ms_current_timestamp()-lastupdate) < 8 weeks
# And somehow display/link to old problems somewhere else...
my $current = [];
if (@$current_map < 9) {
my $limit = 9 - @$current_map;
$current = select_all(
"select id, title, easting, northing, distance
from problem_find_nearby(?, ?, $dist) as nearby, problem
where nearby.problem_id = problem.id
and state = 'confirmed'" . (@ids ? ' and id not in (' . join(',' , @ids) . ')' : '') . "
order by distance, created desc limit $limit", $mid_e, $mid_n);
foreach (@$current) {
my $px = Page::os_to_px($_->{easting}, $x);
my $py = Page::os_to_px($_->{northing}, $y, 1);
$pins .= Page::display_pin($q, $px, $py, 'red', $count_prob++);
}
}
my $fixed = select_all(
"select id, title, easting, northing, distance
from problem_find_nearby(?, ?, $dist) as nearby, problem
where nearby.problem_id = problem.id and state='fixed'
order by created desc limit 9", $mid_e, $mid_n);
foreach (@$fixed) {
my $px = Page::os_to_px($_->{easting}, $x);
my $py = Page::os_to_px($_->{northing}, $y, 1);
$pins .= Page::display_pin($q, $px, $py, 'green', $count_fixed++);
}
return ($pins, $current_map, $current, $fixed, $dist);
}