Skip to content

Commit e719d0a

Browse files
committed
Merge branch 'aberdeenshire-confirm-photos' into staging
2 parents c7a7a22 + fc3f846 commit e719d0a

16 files changed

+626
-83
lines changed

perllib/Integrations/Confirm.pm

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,57 @@ has 'server_timezone' => (
142142
);
143143

144144

145-
=head2 completion_statuses
145+
=head2 enquiry_update_job_photo_statuses
146146
147-
A list of enquiry status codes that determine whether job completion photos
148-
should be looked up when fetching updates.
147+
A list of enquiry status codes that determine whether job photos
148+
should be returned when fetching enquiry updates.
149149
150150
=cut
151151

152-
has completion_statuses => (
152+
has enquiry_update_job_photo_statuses => (
153153
is => 'lazy',
154-
default => sub { $_[0]->config->{completion_statuses} || [] }
154+
default => sub { $_[0]->config->{enquiry_update_job_photo_statuses} || [] }
155155
);
156156

157+
158+
=head2 enquiry_update_defect_photo_statuses
159+
160+
A list of enquiry status codes that determine whether defect photos
161+
should be returned when fetching enquiry updates.
162+
163+
=cut
164+
165+
has enquiry_update_defect_photo_statuses => (
166+
is => 'lazy',
167+
default => sub { $_[0]->config->{enquiry_update_defect_photo_statuses} || [] }
168+
);
169+
170+
171+
=head2 defect_update_job_photo_statuses
172+
173+
A list of job status codes that determine whether job photos
174+
should be returned when fetching defect updates.
175+
176+
=cut
177+
178+
has defect_update_job_photo_statuses => (
179+
is => 'lazy',
180+
default => sub { $_[0]->config->{defect_update_job_photo_statuses} || [] }
181+
);
182+
183+
184+
=head2 include_photos_on_defect_fetch
185+
186+
Whether or not to include defect photos when they are fetched in get_service_requests.
187+
188+
=cut
189+
190+
has include_photos_on_defect_fetch => (
191+
is => 'lazy',
192+
default => sub { $_[0]->config->{include_photos_on_defect_fetch} }
193+
);
194+
195+
157196
=head2 external_system_number
158197
159198
A code to use to mark enquiries we submit as coming from us; with this set,
@@ -584,6 +623,8 @@ sub defects_graphql_query { # XXX factor together with jobs?
584623
}
585624
documents {
586625
url
626+
documentName
627+
documentDate
587628
}
588629
description
589630
}
@@ -639,6 +680,11 @@ sub defect_status_logs_graphql_query {
639680
key
640681
641682
job {
683+
documents {
684+
url
685+
documentName
686+
documentDate
687+
}
642688
defects(filter: {
643689
defectTypeCode: {
644690
inList: [ $defect_type_codes_str ]
@@ -1149,4 +1195,12 @@ sub get_job_photo {
11491195
return ( $type, $response->decoded_content );
11501196
}
11511197

1198+
sub get_photo_by_doc_url {
1199+
my ($self, $doc_url) = @_;
1200+
my $response = $self->web_api_call($doc_url) or return;
1201+
my $type = $response->header('Content-Type') || '';
1202+
return unless $type =~ m{image/(jpeg|pjpeg|gif|tiff|png)}i;
1203+
return ( $type, $response->decoded_content );
1204+
}
1205+
11521206
1;

perllib/Open311/Endpoint/Integration/Confirm.pm

Lines changed: 137 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use Integrations::Confirm;
1515

1616
use Path::Tiny;
1717
use SOAP::Lite; # +trace => [ qw/method debug/ ];
18+
use URI::Escape;
1819

1920

2021
has jurisdiction_id => (
@@ -614,18 +615,39 @@ sub get_service_request_updates {
614615
centralEnquiry {
615616
subjectCode
616617
serviceCode
618+
enquiryLink {
619+
job {
620+
documents {
621+
url
622+
documentName
623+
documentDate
624+
}
625+
}
626+
defect {
627+
documents {
628+
url
629+
documentName
630+
documentDate
631+
}
632+
}
617633
}
618634
}
619635
}
620636
GRAPHQL
621637
my $results =$integ->perform_request_graphql(query => $query)->{data}->{enquiryStatusLogs};
622638
for my $status_log (@$results) {
623639
# remap from GraphQL key names
640+
641+
my @job_documents = $self->_parse_graphql_docs($status_log->{centralEnquiry}{enquiryLink}{job}{documents});
642+
my @defect_documents = $self->_parse_graphql_docs($status_log->{centralEnquiry}{enquiryLink}{defect}{documents});
643+
624644
my $log = {
625645
EnquiryLogNumber => $status_log->{logNumber},
626646
LoggedTime => $status_log->{loggedDate},
627647
StatusLogNotes => $status_log->{notes},
628648
EnquiryStatusCode => $status_log->{enquiryStatusCode},
649+
JobDocuments => \@job_documents,
650+
DefectDocuments => \@defect_documents,
629651
};
630652

631653
# The enquiry's service/subject codes may have changed with this
@@ -718,6 +740,8 @@ sub _get_service_request_updates_for_defects {
718740
end_date => $args->{end_date},
719741
);
720742

743+
my %statuses_for_job_photos = map { $_ => 1 } @{ $integ->defect_update_job_photo_statuses };
744+
721745
for my $log ( @{$status_logs} ) {
722746
my $status
723747
= $self->job_reverse_status_mapping->{ $log->{statusCode} };
@@ -736,6 +760,16 @@ sub _get_service_request_updates_for_defects {
736760
->truncate( to => 'second' );
737761
$dt->set_time_zone( $integ->server_timezone );
738762

763+
764+
my @media_urls;
765+
if ($statuses_for_job_photos{$log->{statusCode}}) {
766+
my @job_docs = $self->_parse_graphql_docs($log->{job}{documents});
767+
my @filtered = $self->filter_photos_graphql(@job_docs);
768+
my @urls = map { $self->construct_photo_url_from_graphql_fetched_doc($_) } @filtered;
769+
push @media_urls, @urls;
770+
}
771+
772+
# NOTE: Only the first media_url in the array will actually be returned.
739773
for my $defect ( @{$log->{job}->{defects}} ) {
740774
push @$updates,
741775
Open311::Endpoint::Service::Request::Update::mySociety->new(
@@ -745,33 +779,89 @@ sub _get_service_request_updates_for_defects {
745779
updated_datetime => $dt,
746780
external_status_code => $log->{statusCode},
747781
description => $defect->{targetDate} || '',
782+
@media_urls ? ( media_url => \@media_urls ) : (),
748783
);
749784
}
750785
}
751786
}
752787

788+
sub construct_photo_url {
789+
my ($self, $query_string) = @_;
790+
791+
my $integ = $self->get_integration;
792+
my $base_url = $integ->config->{base_url};
793+
my $tenant_id = $integ->config->{tenant_id};
794+
my $jurisdiction_id = $self->jurisdiction_id;
795+
796+
return $base_url ."photos?jurisdiction_id=$jurisdiction_id&" . $query_string;
797+
}
798+
799+
sub construct_photo_url_from_rest_fetched_job {
800+
my ($self, $job_id, $doc_num) = @_;
801+
return $self->construct_photo_url("job=$job_id&photo=$doc_num");
802+
}
803+
804+
sub construct_photo_url_from_graphql_fetched_doc {
805+
my ($self, $doc) = @_;
806+
807+
my $integ = $self->get_integration;
808+
my $tenant_id = $integ->config->{tenant_id};
809+
810+
# GraphQL document URLs appear to include the full web api URL and tenant
811+
# e.g. /ConfirmWeb/api/<tenant>/atttachments/...
812+
# Strip this if present.
813+
my $doc_url = ($doc->{URL} =~ /$tenant_id(.*)/) ? $1 : $doc->{URL};
814+
815+
my $qs = "doc_url=" . uri_escape($doc_url);
816+
return $self->construct_photo_url($qs);
817+
}
818+
753819
sub photo_filter {
754820
my ($self, $doc) = @_;
755821
return $doc->{fileName} =~ /jpe?g/i;
756822
}
757823

758-
sub photo_urls_for_update {
759-
my ($self, $enquiry_id) = @_;
760-
my $integ = $self->get_integration;
824+
sub filter_photos_graphql {
825+
my ($self, @graphql_docs) = @_;
826+
return grep { $_->{Name} =~ /\.(jpg|jpeg|pjpeg|gif|tiff|png)$/ } @graphql_docs;
827+
}
828+
829+
sub job_photo_urls_for_enquiry_update {
830+
my ($self, $status_log, $enquiry_id) = @_;
831+
832+
if (defined $status_log->{JobDocuments}) {
833+
# We've already queried for the documents in GraphQL.
834+
my @docs = $self->filter_photos_graphql(@{$status_log->{JobDocuments}});
835+
my @urls = map { $self->construct_photo_url_from_graphql_fetched_doc($_) } @docs;
836+
return \@urls;
837+
};
838+
761839

762-
my $enquiry = $integ->get_enquiry_json($enquiry_id) or return;
840+
my $integ = $self->get_integration;
841+
my $enquiry = $integ->get_enquiry_json($enquiry_id) or return [];
763842
my $job_id = $enquiry->{jobNumber};
764-
my $documents = $integ->documents_for_job($job_id) or return;
843+
my $documents = $integ->documents_for_job($job_id) or return [];
765844

766845
my @ids = map { $_->{documentNo} } grep { $self->photo_filter($_) } @$documents;
767-
return unless @ids;
768-
769-
my $jurisdiction_id = $self->jurisdiction_id;
770-
my @urls = map { $integ->config->{base_url} . "photo/completion?jurisdiction_id=$jurisdiction_id&job=$job_id&photo=$_" } @ids;
846+
return [] unless @ids;
771847

848+
my @urls = map { $self->construct_photo_url_from_rest_fetched_job($job_id, $_) } @ids;
772849
return \@urls;
773850
}
774851

852+
sub defect_photo_urls_for_enquiry_update {
853+
my ($self, $status_log) = @_;
854+
855+
if (defined $status_log->{DefectDocuments}) {
856+
# We've already queried for the documents in GraphQL.
857+
my @docs = $self->filter_photos_graphql(@{$status_log->{DefectDocuments}});
858+
my @urls = map { $self->construct_photo_url_from_graphql_fetched_doc($_) } @docs;
859+
return \@urls;
860+
};
861+
862+
die "Fetching defect photos for an enquiry update when not using graphql is unimplemented.";
863+
}
864+
775865
sub services {
776866
my $self = shift;
777867
my @services = $self->_services;
@@ -1147,7 +1237,8 @@ sub _parse_enquiry {
11471237
sub _parse_enquiry_status_log {
11481238
my ($self, $status_log, $enquiry_id, $integ, $extras) = @_;
11491239

1150-
my %completion_statuses = map { $_ => 1} @{ $integ->completion_statuses };
1240+
my %statuses_for_job_photos = map { $_ => 1 } @{ $integ->enquiry_update_job_photo_statuses };
1241+
my %statuses_for_defect_photos = map { $_ => 1 } @{ $integ->enquiry_update_defect_photo_statuses };
11511242

11521243
my $update_id = $enquiry_id . "_" . $status_log->{EnquiryLogNumber};
11531244
my $ts = $self->date_parser->parse_datetime($status_log->{LoggedTime})->truncate( to => 'second' );
@@ -1162,21 +1253,23 @@ sub _parse_enquiry_status_log {
11621253
$status = "open";
11631254
}
11641255

1165-
my $media_urls;
1166-
if ($completion_statuses{$status_log->{EnquiryStatusCode}}) {
1167-
# This enquiry has been marked as complete by this update;
1168-
# see if there's a photo.
1169-
$media_urls = $self->photo_urls_for_update($enquiry_id);
1256+
my @media_urls;
1257+
if ($statuses_for_job_photos{$status_log->{EnquiryStatusCode}}) {
1258+
push @media_urls, @{$self->job_photo_urls_for_enquiry_update($status_log, $enquiry_id)};
1259+
}
1260+
if ($statuses_for_defect_photos{$status_log->{EnquiryStatusCode}}) {
1261+
push @media_urls, @{$self->defect_photo_urls_for_enquiry_update($status_log)};
11701262
}
11711263

1264+
# NOTE: Only the first media_url in the array will actually be returned.
11721265
return Open311::Endpoint::Service::Request::Update::mySociety->new(
11731266
status => $status,
11741267
update_id => $update_id,
11751268
service_request_id => $enquiry_id,
11761269
description => $description,
11771270
updated_datetime => $ts,
11781271
external_status_code => $status_log->{EnquiryStatusCode},
1179-
$media_urls ? ( media_url => $media_urls ) : (),
1272+
@media_urls ? ( media_url => \@media_urls ) : (),
11801273
$extras ? ( extras => $extras ) : (),
11811274
);
11821275
}
@@ -1349,6 +1442,14 @@ sub _get_service_requests_for_defects {
13491442

13501443
my $description = $self->_description_for_defect($defect, $service);
13511444

1445+
my @media_urls;
1446+
if ($integ->include_photos_on_defect_fetch) {
1447+
my @defect_docs = $self->_parse_graphql_docs($defect->{documents});
1448+
my @filtered = $self->filter_photos_graphql(@defect_docs);
1449+
my @urls = map { $self->construct_photo_url_from_graphql_fetched_doc($_) } @filtered;
1450+
push @media_urls, @urls;
1451+
}
1452+
13521453
my %args = (
13531454
service => $service,
13541455
service_request_id => 'DEFECT_' . $defect_id,
@@ -1359,6 +1460,7 @@ sub _get_service_requests_for_defects {
13591460
# enquiries above
13601461
latlong => [ $defect->{northing}, $defect->{easting} ],
13611462
status => $status,
1463+
@media_urls ? ( media_url => \@media_urls ) : (),
13621464
);
13631465

13641466
my $request = $self->new_request( %args );
@@ -1547,12 +1649,18 @@ sub _wrap_services {
15471649
return @services;
15481650
}
15491651

1550-
sub get_completion_photo {
1652+
sub get_photo {
15511653
my ($self, $args) = @_;
15521654

1553-
my ($content_type, $content) = $self->get_integration->get_job_photo($args->{job}, $args->{photo});
1554-
return [ 404, [ 'Content-type', 'text/plain' ], [ 'Not found' ] ] unless $content;
1655+
my $content_type;
1656+
my $content;
1657+
if ($args->{job}) {
1658+
($content_type, $content) = $self->get_integration->get_job_photo($args->{job}, $args->{photo});
1659+
} elsif ($args->{doc_url}) {
1660+
($content_type, $content) = $self->get_integration->get_photo_by_doc_url($args->{doc_url});
1661+
}
15551662

1663+
return [ 404, [ 'Content-type', 'text/plain' ], [ 'Not found' ] ] unless $content;
15561664
return [ 200, [ 'Content-type', $content_type ], [ $content ] ];
15571665
}
15581666

@@ -1579,4 +1687,14 @@ sub _parse_start_end_dates {
15791687
return ($start, $end);
15801688
}
15811689

1690+
sub _parse_graphql_docs {
1691+
my ($self, $docs) = @_;
1692+
return () unless $docs;
1693+
return map { {
1694+
URL => $_->{url},
1695+
Name => $_->{documentName},
1696+
Date => $self->date_parser->parse_datetime($_->{documentDate})
1697+
} } @$docs;
1698+
}
1699+
15821700
1;

perllib/Open311/Endpoint/Integration/UK.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package Open311::Endpoint::Integration::UK;
33
use Moo;
44
extends 'Open311::Endpoint';
55
with 'Open311::Endpoint::Role::mySociety';
6-
with 'Open311::Endpoint::Role::CompletionPhotos';
6+
with 'Open311::Endpoint::Role::Photos';
77

88
use Types::Standard ':all';
99
use Module::Pluggable

0 commit comments

Comments
 (0)