Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file sort for last created annotations #903

Merged
merged 14 commits into from
Sep 5, 2024
32 changes: 32 additions & 0 deletions app/Http/Controllers/Api/VolumeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,36 @@ public function clone(CloneVolume $request)
->with('messageType', 'success');

}

/**
* Return volume's annotation timestamps of newest annotations
*
* @param int $id
* @return object
* @api {get} volumes{/id}/files/annotation-timestamps Get the newest annotation timestamps of the volume
* @apiGroup Volumes
mzur marked this conversation as resolved.
Show resolved Hide resolved
* @apiPermission projectMember
*
* @apiParam {Number} id The volume ID.
*
* @apiSuccessExample {json} Success response:
* {
* 1: "2024-08-23T07:06:31.000000Z",
* 2: "2024-08-23T06:40:29.000000Z",
* 3: "2024-08-23T06:40:35.000000Z",
* }
*
*/
public function getAnnotationTimestamps($id)
{
$volume = Volume::findOrFail($id);
$this->authorize('access', $volume);

$timestamps = $volume->files
->flatMap(fn ($file) => $file->annotations)
->groupBy('file_id')
->map(fn ($annotations) => $annotations->max('created_at'));
mzur marked this conversation as resolved.
Show resolved Hide resolved

return $timestamps;
}
}
4 changes: 4 additions & 0 deletions resources/assets/js/volumes/api/volumes.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,9 @@ export default Vue.resource('api/v1/volumes{/id}', {}, {
clone: {
method: 'POST',
url: 'api/v1/volumes{/id}/clone-to{/project_id}'
},
getAnnotationTimestamps:{
method: 'GET',
url: 'api/v1/volumes{/id}/files/annotation-timestamps'
}
});
40 changes: 40 additions & 0 deletions resources/assets/js/volumes/stores/sorters.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SortComponent from '../components/sortComponent';
import VolumeApi from '../api/volumes';

let filenameSorter = {
id: 'filename',
Expand Down Expand Up @@ -99,6 +100,44 @@ let randomSorter = {
},
};

let annotationTime = {
id: 'annotationTime',
types: ['image', 'video'],
component: {
mixins: [SortComponent],
data() {
return {
volumeId: -1,
fileIds: [],
title: 'Sort images by last created annotation',
text: 'Last annotated',
id: 'annotationTime',

};
},
methods: {
getSequence() {
return VolumeApi.getAnnotationTimestamps({'id': this.volumeId})
.then((res) => res.body)
.then((timestamps) => {
let sorted = Object.entries(timestamps).sort(this.compare);
sorted = sorted.map(e => parseInt(e[0]));
let diff = this.fileIds.filter(id => !sorted.includes(id));
sorted = sorted.concat(diff);
return sorted;
});
},
compare(a, b) {
return Date.parse(b[1]) - Date.parse(a[1]);
}
},
created() {
this.volumeId = biigle.$require('volumes.volumeId');
this.fileIds = biigle.$require('volumes.fileIds');
},
},
};

/**
* Store for the volume image sorters
*/
Expand All @@ -107,4 +146,5 @@ export default [
filenameSorter,
idSorter,
randomSorter,
annotationTime,
];
4 changes: 4 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@
'volumes/{id}/clone-to/{id2}', 'VolumeController@clone'
);

$router->get(
'volumes/{id}/files/annotation-timestamps', 'VolumeController@getAnnotationTimestamps'
);

$router->resource('volumes', 'VolumeController', [
'only' => ['index', 'show', 'update'],
'parameters' => ['volumes' => 'id'],
Expand Down
50 changes: 50 additions & 0 deletions tests/php/Http/Controllers/Api/VolumeControllerTest.php
mzur marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
use Biigle\Jobs\ProcessNewVolumeFiles;
use Biigle\MediaType;
use Biigle\Role;
use Biigle\Tests\ImageAnnotationTest;
use Biigle\Tests\ImageTest;
use Biigle\Tests\ProjectTest;
use Biigle\Tests\UserTest;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Queue;
Expand Down Expand Up @@ -306,4 +309,51 @@ public function testCloneVolume()
$response->assertStatus(201);
Queue::assertPushed(CloneImagesOrVideos::class);
}

public function testGetAnnotationTimestamps()
{

$volume = $this
->volume([
'created_at' => '2022-11-09 14:37:00',
'updated_at' => '2022-11-09 14:37:00',
])
->fresh();

$img = ImageTest::create([
'filename' => 'test123.jpg',
'volume_id' => $volume->id,
]);
ImageAnnotationTest::create([
'created_at' => '2023-11-09 06:37:00',
'image_id' => $img->id,
]);
ImageAnnotationTest::create([
'created_at' => '2020-11-09 06:37:00',
'image_id' => $img->id,
]);
$img2 = ImageTest::create([
'filename' => 'test321.jpg',
'volume_id' => $volume->id
]);
ImageAnnotationTest::create([
'created_at' => '2024-11-09 14:37:00',
'image_id' => $img2->id,
]);

$this->be(UserTest::create());
$this->getJson("/api/v1/volumes/{$volume->id}/files/annotation-timestamps")->assertForbidden();

$this->beGuest();
$response = $this->getJson("/api/v1/volumes/{$volume->id}/files/annotation-timestamps");

$response->assertSuccessful();
$content = json_decode($response->getContent(), true);

$this->assertCount(2, $content);
$this->assertNotNull($content[$img->id]);
$this->assertNotNull($content[$img2->id]);
$this->assertSame($content[$img->id], "2023-11-09T05:37:00.000000Z");
$this->assertSame($content[$img2->id], "2024-11-09T13:37:00.000000Z");
}
}