Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions perllib/Open311/Endpoint/Integration/AlloyV2.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1773,6 +1773,59 @@ sub _get_attachments {
return @photos;
}

=head2 get_photo

Fetch a photo from Alloy by its file item ID.

=cut

sub get_photo {
my ($self, $args) = @_;

my $item_id = $args->{item};
unless ($item_id) {
$self->logger->error("get_photo called without item parameter");
return [ 400, [ 'Content-Type', 'text/plain' ], [ 'Missing item parameter' ] ];
}

# Fetch the file from Alloy
my $content;
my $content_type = 'image/jpeg'; # default

# First, get the file metadata to determine content type
try {
my $file_item = $self->alloy->api_call(call => "item/$item_id");
if ($file_item && $file_item->{item}) {
my $attrs = $self->alloy->attributes_to_hash($file_item->{item});
my $filename = $attrs->{attributes_filesOriginalName} || '';
if ($filename =~ /\.png$/i) {
$content_type = 'image/png';
} elsif ($filename =~ /\.gif$/i) {
$content_type = 'image/gif';
} elsif ($filename =~ /\.jpe?g$/i) {
$content_type = 'image/jpeg';
}
}
} catch {
$self->logger->warn("Failed to fetch file metadata for $item_id: $_");
};

# Now fetch the actual file content
try {
$content = $self->alloy->api_call(
call => "file/$item_id",
raw => 1,
);
} catch {
$self->logger->error("Failed to fetch photo $item_id: $_");
return [ 404, [ 'Content-Type', 'text/plain' ], [ 'Photo not found' ] ];
};

return [ 404, [ 'Content-Type', 'text/plain' ], [ 'Photo not found' ] ] unless $content;
return [ 200, [ 'Content-Type', $content_type ], [ $content ] ];
}


sub upload_media {
my ($self, $args) = @_;

Expand Down
34 changes: 34 additions & 0 deletions perllib/Open311/Endpoint/Integration/UK/Bristol/Alloy.pm
Original file line number Diff line number Diff line change
Expand Up @@ -174,5 +174,39 @@ sub _get_inspection_status {
return ($status, $ext_code);
}

=head2 _get_inspection_updates_design

Override to add media_url support for photos attached to the item.

=cut

sub _get_inspection_updates_design {
my ($self, $design, $args) = @_;

# Call parent to get the base updates and the raw items
my ($updates_ref, $items_by_id) = $self->SUPER::_get_inspection_updates_design($design, $args);
my @updates = @$updates_ref;

# Build attachment cache once for all updates
# This avoids making individual API calls for each attachment
# This also filters out photos that seem to have originated from FMS (based on filename)
my $cache = $self->_build_attachment_cache($args);

# For each update, use the already-fetched resource to add media URLs
for my $update (@updates) {
my $service_request_id = $update->service_request_id;

next unless $update->status eq 'fixed'; # only show photos for fixed updates

# Use the item already fetched by the parent method
my $report = $items_by_id->{$service_request_id};

if ($report) {
$update->{media_url} = $self->_media_urls_for_item({item => $report}, $cache, $args);
}
}

return (\@updates, $items_by_id);
}

1;
52 changes: 0 additions & 52 deletions perllib/Open311/Endpoint/Integration/UK/Dumfries.pm
Original file line number Diff line number Diff line change
Expand Up @@ -331,58 +331,6 @@ sub _get_inspection_updates_design {
return (\@updates, $items_by_id);
}

=head2 get_photo

Fetch a photo from Alloy by its file item ID.

=cut

sub get_photo {
my ($self, $args) = @_;

my $item_id = $args->{item};
unless ($item_id) {
$self->logger->error("get_photo called without item parameter");
return [ 400, [ 'Content-Type', 'text/plain' ], [ 'Missing item parameter' ] ];
}

# Fetch the file from Alloy
my $content;
my $content_type = 'image/jpeg'; # default

# First, get the file metadata to determine content type
try {
my $file_item = $self->alloy->api_call(call => "item/$item_id");
if ($file_item && $file_item->{item}) {
my $attrs = $self->alloy->attributes_to_hash($file_item->{item});
my $filename = $attrs->{attributes_filesOriginalName} || '';
if ($filename =~ /\.png$/i) {
$content_type = 'image/png';
} elsif ($filename =~ /\.gif$/i) {
$content_type = 'image/gif';
} elsif ($filename =~ /\.jpe?g$/i) {
$content_type = 'image/jpeg';
}
}
} catch {
$self->logger->warn("Failed to fetch file metadata for $item_id: $_");
};

# Now fetch the actual file content
try {
$content = $self->alloy->api_call(
call => "file/$item_id",
raw => 1,
);
} catch {
$self->logger->error("Failed to fetch photo $item_id: $_");
return [ 404, [ 'Content-Type', 'text/plain' ], [ 'Photo not found' ] ];
};

return [ 404, [ 'Content-Type', 'text/plain' ], [ 'Photo not found' ] ] unless $content;
return [ 200, [ 'Content-Type', $content_type ], [ $content ] ];
}

=head2 _append_attachments_to_defect

Appends new attachment IDs to a defect's existing attachments.
Expand Down
76 changes: 76 additions & 0 deletions t/open311/endpoint/bristol_alloy.t
Original file line number Diff line number Diff line change
Expand Up @@ -466,4 +466,80 @@ subtest "check fetch updates" => sub {



subtest "check fetch updates includes media_url for fixed status" => sub {
$integration->mock('search', sub {
my ($self, $body) = @_;
if ($body->{properties}{dodiCode} eq 'designs_files') {
return [
{
itemId => '63e0f01cdce34826965f3039',
createdDate => '2023-02-16T13:50:00.000Z',
attributes => [
{ attributeCode => 'attributes_filesOriginalName', value => 'photo.jpg' }, # BCC inspector photo
],
},
{
itemId => '631cdce34826965f3039e0f0',
createdDate => '2023-02-16T13:50:00.000Z',
attributes => [
{ attributeCode => 'attributes_filesOriginalName', value => '123456.0.full.jpeg' }, # FMS report photo
],
},
];
}
return [
{ # update in fixed state but with FMS photo as well as inspector photo
itemId => '63ee34826965f30390f01cdc',
designCode => 'designs_bWCSCStreetCleansingDefect_5e21a98bca315003e0983035',
attributes => [
{ attributeCode => 'attributes_defectsStatus', value => ['5c8bdfc88ae862230019dc22'] },
{ attributeCode => 'attributes_filesAttachableAttachments', value => ['63e0f01cdce34826965f3039', '631cdce34826965f3039e0f0'] },
],
},
{ # update in action scheduled state with inspector photo
itemId => '63ee34826965f30390f01cda',
designCode => 'designs_bWCSCStreetCleansingDefect_5e21a98bca315003e0983035',
attributes => [
{ attributeCode => 'attributes_defectsStatus', value => ['5c8bdfb58ae862230019dc1f'] },
{ attributeCode => 'attributes_filesAttachableAttachments', value => ['63e0f01cdce34826965f3039'] },
],
},
];
});

my $res = $endpoint->run_test_request(
GET => '/servicerequestupdates.json?jurisdiction_id=dummy&start_date=2023-02-16T07:43:46Z&end_date=2023-02-16T19:43:46Z',
);
ok $res->is_success, 'valid request' or diag $res->content;

my $updates = decode_json($res->content);

is_deeply $updates, [
{
description => '',
extras => {
latest_data_only => 1
},
media_url => 'http://localhost/photos?jurisdiction_id=bristol_alloy&item=63e0f01cdce34826965f3039', # FMS photo is not included
service_request_id => '63ee34826965f30390f01cdc',
status => 'fixed',
update_id => '63ee34826965f30390f01cdc_20230216135008792',
updated_datetime => '2023-02-16T13:50:08Z',
},
{
description => '',
extras => {
latest_data_only => 1
},
media_url => '', # no photos for action scheduled update
service_request_id => '63ee34826965f30390f01cda',
status => 'action_scheduled',
update_id => '63ee34826965f30390f01cda_20230216135008792',
updated_datetime => '2023-02-16T13:50:08Z',
}
];

$integration->unmock('search');
};

done_testing;
1 change: 1 addition & 0 deletions t/open311/endpoint/bristol_alloy.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"api_key": "api_key",
"api_url": "http://localhost/api/",
"base_url": "http://localhost/",

"rfs_design": {
"SC-Fly-Post Defect": 'designs_bWCSCFlyPostDefect_5e203bd4ca315009b4e5c714',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"page": 1,
"pageSize": 20,
"results": [
{
"itemId": "63ee34826965f30390f01cdc",
"designCode": "designs_bWCSCStreetCleansingDefect_5e21a98bca315003e0983035",
"action": "Edit",
"causes": [
{
"workflowRunId": "63ee348be5b28203ae91632d",
"discriminator": "ItemChangeCauseWorkflowWebModel"
}
],
"date": "2023-02-16T13:50:08.792Z",
"username": "alloybot"
}
]
}
Loading