Skip to content

Commit

Permalink
[Brent] Add Brent responsibility layer
Browse files Browse the repository at this point in the history
Calls an asset layer that covers Brent and overlaps the boundaries
and calculates responsibilities for the picked area.

If the area is covered by the Brent asset, but is in another
council's area, Brent's Symology categories should be combined
with the categories usually present for that point so reports
can be made to Brent although not officially their area
  • Loading branch information
MorayMySoc committed Nov 28, 2023
1 parent 3192a0d commit cf6b2cf
Show file tree
Hide file tree
Showing 7 changed files with 460 additions and 14 deletions.
173 changes: 170 additions & 3 deletions perllib/FixMyStreet/Cobrand/Brent.pm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use DateTime::Format::Strptime;
use Try::Tiny;
use LWP::Simple;
use URI;
use JSON::MaybeXS;

=head1 INTEGRATIONS
Expand All @@ -46,7 +47,8 @@ with 'FixMyStreet::Roles::CobrandEcho';
with 'FixMyStreet::Roles::SCP';
use Integrations::Paye;

sub council_area_id { return 2488; }
# Brent covers some of the areas around it so that it can handle near-boundary reports
sub council_area_id { return [2488, 2505, 2489, 2487]; } # 2505 Camden, 2489 Barnet, 2487 Harrow
sub council_area { return 'Brent'; }
sub council_name { return 'Brent Council'; }
sub council_url { return 'brent'; }
Expand Down Expand Up @@ -135,6 +137,8 @@ sub disambiguate_location { {

=item * Filters down search results to be the street name and the postcode only
=back
=cut

sub geocoder_munge_results {
Expand All @@ -143,7 +147,140 @@ sub geocoder_munge_results {
$result->{display_name} =~ s/, London Borough of Brent, London, Greater London, England//;
}

=back
=head2 check_report_is_on_cobrand_asset
If the location is covered by an area of differing responsibility (e.g. Brent
in Camden, or Camden in Brent), return true (either 1 if an area name is
provided, or the name of the area if not).
=cut

sub check_report_is_on_cobrand_asset {
my ($self, $council_area) = shift @_;

my $lat = $self->{c}->stash->{latitude};
my $lon = $self->{c}->stash->{longitude};
my $host = FixMyStreet->config('STAGING_SITE') ? "tilma.staging.mysociety.org" : "tilma.mysociety.org";

my $cfg = {
url => "https://$host/mapserver/brent",
srsname => "urn:ogc:def:crs:EPSG::4326",
typename => "BrentDiffs",
filter => "<Filter><Contains><PropertyName>Geometry</PropertyName><gml:Point><gml:coordinates>$lon,$lat</gml:coordinates></gml:Point></Contains></Filter>",
outputformat => 'GML3',
};

my $features = $self->_fetch_features($cfg, -1, -1, 1);

if ($$features[0]) {
if ($council_area) {
if ($$features[0]->{'ms:BrentDiffs'}->{'ms:name'} eq $council_area) {
return 1;
}
} else {
return $$features[0]->{'ms:BrentDiffs'}->{'ms:name'};
}
}
}

=head2 munge_overlapping_asset_bodies
Alters the list of available bodies for the location,
depending on calculated responsibility. After this function,
the bodies list will be the relevant bodies for the point,
though categories may need to be altered later on.
=cut

sub munge_overlapping_asset_bodies {
my ($self, $bodies) = @_;

# in_area will be true if the point is within the administrative area of Brent
my $in_area = grep ($self->council_area_id->[0] == $_, keys %{$self->{c}->stash->{all_areas}});
# cobrand will be true if the point is within an area of different responsibility from the norm
my $cobrand = $self->check_report_is_on_cobrand_asset;

if ($in_area) {
# In the area of Brent...
if (!$cobrand || $cobrand eq 'Brent') {
# ...Brent's responsibility - remove the other bodies covering the Brent area
%$bodies = map { $_->id => $_ } grep {
$_->name ne 'Camden Borough Council' &&
$_->name ne 'Barnet Borough Council' &&
$_->name ne 'Harrow Borough Council'
} values %$bodies;
} else {
# ...someone else's responsibility, take out the ones definitely not responsible
my %cobrands = (Harrow => 'Harrow Borough Council', Camden => 'Camden Borough Council', Barnet => 'Barnet Borough Council');
my $selected = $cobrands{$cobrand};
%$bodies = map { $_->id => $_ } grep {
$_->name eq $selected || $_->name eq 'Brent Council' || $_->name eq 'TfL' || $_->name eq 'National Highways'
} values %$bodies;
}
} else {
# Not in the area of Brent...
if (!$cobrand || $cobrand ne 'Brent') {
# ...not Brent's responsibility - remove Brent
%$bodies = map { $_->id => $_ } grep {
$_->name ne 'Brent Council'
} values %$bodies;
} else {
# ...Brent's responsibility - leave (both) bodies alone
}
}
}

=head2 munge_cobrand_asset_categories
If we're in an overlapping area, we want to take the street categories
of one body, and the non-street categories of the other.
=cut

sub munge_cobrand_asset_categories {
my ($self, $contacts) = @_;

my %bodies = map { $_->body->name => $_->body } @$contacts;
my %non_street = (
'Barnet' => { map { $_ => 1 } @{ $self->_barnet_non_street } },
'Camden' => { map { $_ => 1 } @{ $self->_camden_non_street } },
'Harrow' => { map { $_ => 1 } @{ $self->_harrow_non_street } },
);

# in_area will be true if the point is within the administrative area of Brent
my $in_area = grep ($self->council_area_id->[0] == $_, keys %{$self->{c}->stash->{all_areas}});
# cobrand will be true if the point is within an area of different responsibility from the norm
my $cobrand = $self->check_report_is_on_cobrand_asset || '';
return unless $cobrand;

my $brent_body = $self->body->id;
if (!$in_area && $cobrand eq 'Brent') {
# Outside the area of Brent, but Brent's responsibility
my $area;
if (grep {$_->{name} eq 'Camden Borough Council'} values %{$self->{c}->stash->{all_areas}}){
$area = 'Camden';
} elsif (grep {$_->{name} eq 'Harrow Borough Council'} values %{$self->{c}->stash->{all_areas}}) {
$area = 'Harrow';
} elsif (grep {$_->{name} eq 'Barnet Borough Council'} values %{$self->{c}->stash->{all_areas}}) {
$area = 'Barnet';
};
my $other_body = $bodies{$area . " Borough Council"};

# Remove the non-street contacts of Brent
@$contacts = grep { !($_->email !~ /^Symology/ && $_->body_id == $brent_body) } @$contacts;
# Remove the street contacts of the other
@$contacts = grep { !(!$non_street{$area}{$_->category} && $_->body_id == $other_body->id) } @$contacts
if $other_body;
} elsif ($in_area && $cobrand ne 'Brent') {
# Inside the area of Brent, but not Brent's responsibility
my $other_body = $bodies{$cobrand . " Borough Council"};
# Remove the street contacts of Brent
@$contacts = grep { !($_->email =~ /^Symology/ && $_->body_id == $brent_body) } @$contacts;
# Remove the non-street contacts of the other
@$contacts = grep { !($non_street{$cobrand}{$_->category} && $_->body_id == $other_body->id) } @$contacts
if $other_body;
}
}

=head2 pin_colour
Expand Down Expand Up @@ -1544,7 +1681,6 @@ sub waste_get_pro_rata_cost {
return $self->feature('payment_gateway')->{ggw_cost};
}


sub bulky_collection_time { { hours => 7, minutes => 0 } }
sub bulky_cancellation_cutoff_time { { hours => 23, minutes => 59 } }
sub bulky_cancel_by_update { 1 }
Expand Down Expand Up @@ -1637,4 +1773,35 @@ sub waste_reconstruct_bulky_data {
return $saved_data;
}

sub _barnet_non_street {
return [
'Abandoned vehicles',
'Graffiti',
'Dog fouling',
'Overhanging foliage',
]
};

sub _camden_non_street {
return [
'Abandoned Vehicles',
'Dead animal',
'Flyposting',
'Public toilets',
'Recycling & rubbish (Missed bin)',
'Dott e-bike / e-scooter',
'Human Forest e-bike',
'Lime e-bike / e-scooter',
'Tier e-bike / e-scooter',
]
}

sub _harrow_non_street {
return [
'Abandoned vehicles',
'Car parking',
'Graffiti',
]
}

1;
99 changes: 98 additions & 1 deletion perllib/FixMyStreet/Cobrand/Camden.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use parent 'FixMyStreet::Cobrand::Whitelabel';
use strict;
use warnings;

sub council_area_id { return 2505; }
sub council_area_id { return [2505, 2488]; }
sub council_area { return 'Camden'; }
sub council_name { return 'Camden Council'; }
sub council_url { return 'camden'; }
Expand Down Expand Up @@ -131,6 +131,103 @@ sub user_from_oidc {
return ($name, $email);
}

=head2 check_report_is_on_cobrand_asset
If the location is covered by an area of differing responsibility (e.g. Brent
in Camden, or Camden in Brent), return true (either 1 if an area name is
provided, or the name of the area if not). Identical to function in Brent.pm
=cut

sub check_report_is_on_cobrand_asset {
my ($self, $council_area) = shift @_;

my $lat = $self->{c}->stash->{latitude};
my $lon = $self->{c}->stash->{longitude};
my ($x, $y) = Utils::convert_latlon_to_en($lat, $lon, 'G');
my $host = FixMyStreet->config('STAGING_SITE') ? "tilma.staging.mysociety.org" : "tilma.mysociety.org";

my $cfg = {
url => "https://$host/mapserver/brent",
srsname => "urn:ogc:def:crs:EPSG::27700",
typename => "BrentDiffs",
filter => "<Filter><Contains><PropertyName>Geometry</PropertyName><gml:Point><gml:coordinates>$x,$y</gml:coordinates></gml:Point></Contains></Filter>",
outputformat => 'GML3',
};

my $features = $self->_fetch_features($cfg, $x, $y, 1);

if ($$features[0]) {
if ($council_area) {
if ($$features[0]->{'ms:BrentDiffs'}->{'ms:name'} eq $council_area) {
return 1;
}
} else {
return $$features[0]->{'ms:BrentDiffs'}->{'ms:name'};
}
}
}

=head2 munge_overlapping_asset_bodies
Alters the list of available bodies for the location,
depending on calculated responsibility. Here, we remove the
Brent body if we're inside Camden and it's not a Brent area.
=cut

sub munge_overlapping_asset_bodies {
my ($self, $bodies) = @_;

# in_area will be true if the point is within the administrative area of Camden
my $in_area = scalar(%{$self->{c}->stash->{all_areas}}) == 1 && (values %{$self->{c}->stash->{all_areas}})[0]->{id} eq $self->council_area_id->[0];
# cobrand will be true if the point is within an area of different responsibility from the norm
my $cobrand = $self->check_report_is_on_cobrand_asset;
if ($in_area && !$cobrand) {
# Within Camden, and Camden's responsibility
%$bodies = map { $_->id => $_ } grep {
$_->name ne 'Brent Council'
} values %$bodies;
}
};

=head2 munge_cobrand_asset_categories
If we're in an overlapping area, we want to take the street categories
of one body, and the non-street categories of the other.
=cut

sub munge_cobrand_asset_categories {
my ($self, $contacts) = @_;

# in_area will be true if the point is within the administrative area of Camden
my $in_area = scalar(%{$self->{c}->stash->{all_areas}}) == 1 && (values %{$self->{c}->stash->{all_areas}})[0]->{id} eq $self->council_area_id->[0];
# cobrand will be true if the point is within an area of different responsibility from the norm
my $cobrand = $self->check_report_is_on_cobrand_asset || '';

my $brent = FixMyStreet::Cobrand::Brent->new();
my %non_street = map { $_ => 1 } @{ $brent->_camden_non_street } ;
my $brent_body = $brent->body;
my $camden_body = $self->body;

if ($in_area && $cobrand eq 'Brent') {
# Within Camden, but Brent's responsibility
# Remove the non-street contacts of Brent
@$contacts = grep { !($_->email !~ /^Symology/ && $_->body_id == $brent_body->id) } @$contacts
if $brent_body;
# Remove the street contacts of Camden
@$contacts = grep { !(!$non_street{$_->category} && $_->body_id == $camden_body->id) } @$contacts;
} elsif (!$in_area && $cobrand eq 'Camden') {
# Outside Camden, but Camden's responsibility
# Remove the street contacts of Brent
@$contacts = grep { !($_->email =~ /^Symology/ && $_->body_id == $brent_body->id) } @$contacts
if $brent_body;
# Remove the non-street contacts of Camden
@$contacts = grep { !($non_street{$_->category} && $_->body_id == $camden_body->id) } @$contacts;
}
}

=head2 dashboard_export_problems_add_columns
Has user name and email fields added to their csv export
Expand Down
10 changes: 10 additions & 0 deletions perllib/FixMyStreet/Cobrand/FixMyStreet.pm
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ sub munge_report_new_bodies {
my $bristol = FixMyStreet::Cobrand::Bristol->new({ c => $self->{c} });
$bristol->munge_overlapping_asset_bodies($bodies);
}

if ( $bodies{'Brent Council'} ) {
my $brent = FixMyStreet::Cobrand::Brent->new({ c => $self->{c} });
$brent->munge_overlapping_asset_bodies($bodies);
}
}

sub munge_report_new_contacts {
Expand Down Expand Up @@ -238,6 +243,11 @@ sub munge_report_new_contacts {
my $nh = FixMyStreet::Cobrand::HighwaysEngland->new({ c => $self->{c} });
$nh->national_highways_cleaning_groups($contacts);
}

if ( $bodies{'Brent Council'} ) {
my $brent = FixMyStreet::Cobrand::Brent->new({ c => $self->{c} });
$brent->munge_cobrand_asset_categories($contacts);
}
}

sub munge_unmixed_category_groups {
Expand Down
12 changes: 5 additions & 7 deletions perllib/FixMyStreet/Cobrand/UKCouncils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,8 @@ sub responsible_for_areas {
# This will need changing for two tier councils
if (grep ($self->council_area_id->[0] == $_, keys %$councils)) {
return 1;
} elsif ($self->check_report_is_on_cobrand_asset) {
return 1;
} else {
return 0;
return $self->check_report_is_on_cobrand_asset($self->council_area);
}
} else {
# The majority of cobrands only cover a single area, but e.g. Northamptonshire
Expand Down Expand Up @@ -416,10 +414,7 @@ sub munge_report_new_bodies {
$thamesmead->munge_thamesmead_body($bodies);
}

if ( $bodies{'Bristol City Council'} ) {
my $bristol = FixMyStreet::Cobrand::Bristol->new({ c => $self->{c} });
$bristol->munge_overlapping_asset_bodies($bodies);
}
$self->call_hook(munge_overlapping_asset_bodies => $bodies);
}

sub munge_report_new_contacts {
Expand Down Expand Up @@ -457,6 +452,9 @@ sub munge_report_new_contacts {
my $nh = FixMyStreet::Cobrand::HighwaysEngland->new({ c => $self->{c} });
$nh->national_highways_cleaning_groups($contacts);
}

$self->call_hook(munge_cobrand_asset_categories => $contacts);

}

=item wasteworks_config
Expand Down
1 change: 1 addition & 0 deletions t/Mock/MapIt.pm
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ my @PLACES = (
[ 'CA12 5FJ', 54.60102, -3.13648, 2274, 'Allerdale Borough Council', 'DIS' ],
[ 'NE61 1BE', 55.169081, -1.691012, 2248, 'Northumberland County Council', 'UTA' ],
[ 'SG17 5TQ', 52.03553, -0.36067, 21070, 'Central Bedfordshire Council', 'UTA' ],
[ '?', 51.558568, -0.207702, 2489, 'Barnet Borough Council', 'DIS' ],
);

sub dispatch_request {
Expand Down
Loading

0 comments on commit cf6b2cf

Please sign in to comment.