Skip to content

Commit

Permalink
Merge pull request #903 from biigle/sort-last-annotated
Browse files Browse the repository at this point in the history
Add file sort for last created annotations
  • Loading branch information
mzur authored Sep 5, 2024
2 parents ed5b4b0 + aad4480 commit 65792a4
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 0 deletions.
42 changes: 42 additions & 0 deletions app/Http/Controllers/Api/VolumeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,46 @@ public function clone(CloneVolume $request)
->with('messageType', 'success');

}

/**
* Return file ids which are sorted by annotations.created_at
*
* @param int $id
* @return object
* @api {get} volumes{/id}/files/annotation-timestamps Get file ids sorted by recently annotated
* @apiGroup Volumes
* @apiName VolumeSortByAnnotated
* @apiPermission projectMember
*
* @apiParam {Number} id The volume ID.
*
* @apiSuccessExample {json} Success response:
* {
* 1: 1,
* 2: 2,
* 3: 3,
* }
*
*/
public function getFileIdsSortedByAnnotationTimestamps($id)
{
$volume = Volume::findOrFail($id);
$this->authorize('access', $volume);

if ($volume->isImageVolume()) {
$ids = $volume->files()
->leftJoin('image_annotations', 'images.id', "=", "image_annotations.image_id")
->groupBy('images.id')
->selectRaw('images.id, max(image_annotations.created_at) as created_at')
->orderByRaw("created_at desc nulls last");
} else {
$ids = $volume->files()
->leftJoin('video_annotations', 'videos.id', "=", "video_annotations.video_id")
->groupBy('videos.id')
->selectRaw('videos.id, max(video_annotations.created_at) as created_at')
->orderByRaw("created_at desc nulls last");
}

return $ids->pluck('id');
}
}
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}'
},
getFileIdsSortedByAnnotationTimestamps:{
method: 'GET',
url: 'api/v1/volumes{/id}/files/annotation-timestamps'
}
});
32 changes: 32 additions & 0 deletions resources/assets/js/volumes/stores/sorters.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import SortComponent from '../components/sortComponent';
import VolumeApi from '../api/volumes';
import {handleErrorResponse} from '../../core/messages/store';

let filenameSorter = {
id: 'filename',
Expand Down Expand Up @@ -99,6 +101,35 @@ 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.getFileIdsSortedByAnnotationTimestamps({'id': this.volumeId})
.then((res) => res.body)
.catch(handleErrorResponse);
},
},
created() {
this.volumeId = biigle.$require('volumes.volumeId');
this.fileIds = biigle.$require('volumes.fileIds');
},
},
};

/**
* Store for the volume image sorters
*/
Expand All @@ -107,4 +138,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 @@ -298,6 +298,10 @@
'volumes/{id}/clone-to/{id2}', 'VolumeController@clone'
);

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

$router->resource('volumes', 'VolumeController', [
'only' => ['index', 'show', 'update'],
'parameters' => ['volumes' => 'id'],
Expand Down
120 changes: 120 additions & 0 deletions tests/php/Http/Controllers/Api/VolumeControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
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 Biigle\Tests\VideoAnnotationTest;
use Biigle\Tests\VideoTest;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Queue;
Expand Down Expand Up @@ -314,4 +319,119 @@ public function testCloneVolumeNewName()
$copy = $project->volumes()->first();
$this->assertEquals($copy->name, 'volumecopy');
}

public function testGetImageIdsSortedByAnnotationTimestamps()
{

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

$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(0, $content);

$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,
]);
$img3 = ImageTest::create([
'filename' => 'test321321.jpg',
'volume_id' => $volume->id
]);

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

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

$this->assertCount(3, $content);
$this->assertSame($content[0], $img2->id);
$this->assertSame($content[1], $img->id);
$this->assertSame($content[2], $img3->id);
}

public function testGetVideoIdsSortedByAnnotationTimestamps()
{

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

$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(0, $content);

$video = VideoTest::create([
'filename' => 'test123.jpg',
'volume_id' => $volume->id,
]);
VideoAnnotationTest::create([
'created_at' => '2023-11-09 06:37:00',
'video_id' => $video->id,
]);
VideoAnnotationTest::create([
'created_at' => '2020-11-09 06:37:00',
'video_id' => $video->id,
]);
$video2 = VideoTest::create([
'filename' => 'test321.jpg',
'volume_id' => $volume->id
]);
VideoAnnotationTest::create([
'created_at' => '2024-11-09 14:37:00',
'video_id' => $video2->id,
]);
$video3 = VideoTest::create([
'filename' => 'test321123.jpg',
'volume_id' => $volume->id
]);

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

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

$this->assertCount(3, $content);
$this->assertSame($content[0], $video2->id);
$this->assertSame($content[1], $video->id);
$this->assertSame($content[2], $video3->id);
}
}

0 comments on commit 65792a4

Please sign in to comment.