Skip to content
Open
Show file tree
Hide file tree
Changes from 91 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
4ddad3e
Introduce annotationStrategyTab
dbrembilla Mar 10, 2026
69d1554
Introduces AnnotationStrategyController classes
dbrembilla Mar 10, 2026
35c4693
Introduce annotation_strategies and annotation_strategy_labels tables
dbrembilla Mar 10, 2026
3eee344
Introduce AnnotationStrategy and AnnotationStrategyLabel classes
dbrembilla Mar 10, 2026
7085f7d
Introduce annotationStrategy annotationStrategyLabel resources
dbrembilla Mar 10, 2026
ba65e06
Introduce annotationStrategy to main.js
dbrembilla Mar 10, 2026
e8067e1
Adds api routes
dbrembilla Mar 10, 2026
6919f86
Adds the annotationStrategyLabelImage component
dbrembilla Mar 12, 2026
1efbf56
Finalize the annotationStrategycomponents
dbrembilla Mar 12, 2026
c1a5748
Adds methods to upload and delete reference images
dbrembilla Mar 12, 2026
5db3bbb
Fix migration
dbrembilla Mar 12, 2026
9031cdc
Adds configurations for annotation strategies
dbrembilla Mar 12, 2026
92f9e92
Fix annotationStrategyLabel model
dbrembilla Mar 12, 2026
2c47384
Fix AnnotationStrategyController
dbrembilla Mar 12, 2026
baeacd2
Fix AnnotationStrategy Apis
dbrembilla Mar 12, 2026
0025693
Adds api routes for uploading images
dbrembilla Mar 12, 2026
651ac25
Remove method
dbrembilla Mar 12, 2026
f0fdbd6
Fix css for annotation strategies to make them better centred
dbrembilla Mar 12, 2026
9cb1775
Fix blade template for annotation strategies
dbrembilla Mar 12, 2026
cf80848
Fix annotation strategy tab
dbrembilla Mar 12, 2026
994c6ca
Rename columns in AnnotationStrategy models, adds docstrings
dbrembilla Mar 13, 2026
671bad7
Rename columns, add tests for PHP controllers
dbrembilla Mar 13, 2026
cf5ad6e
Fix style, rename attributes to follow columns
dbrembilla Mar 13, 2026
d27524f
Fix minimum description size
dbrembilla Mar 13, 2026
065a30a
Fix name of attributes
dbrembilla Mar 13, 2026
fd35a40
Fix reload bug
dbrembilla Mar 16, 2026
08c0d65
Format vue files
dbrembilla Mar 16, 2026
c89e608
Fix more formatting
dbrembilla Mar 16, 2026
953cd02
Apply PHP fixing
dbrembilla Mar 16, 2026
17781dc
Add logic when cancel editing in order to avoid cancelling progress
dbrembilla Mar 16, 2026
4fd4bfc
Fix php linting
dbrembilla Mar 16, 2026
564ed9e
Fix access to strategy tab
dbrembilla Mar 17, 2026
5e6f489
Remove unused import
dbrembilla Mar 17, 2026
0489259
Potential fix for pull request finding
dbrembilla Mar 17, 2026
1308ebc
Fix consistency of permission name
dbrembilla Mar 17, 2026
3037631
Fix parameter gathering from request
dbrembilla Mar 17, 2026
164c738
Fix namespace
dbrembilla Mar 17, 2026
690cdf3
Fix docstring
dbrembilla Mar 17, 2026
487226b
Fix typo
dbrembilla Mar 17, 2026
31f62fe
Fix API method in docstring
dbrembilla Mar 17, 2026
4058e99
Fix typo in route
dbrembilla Mar 17, 2026
fe8574a
Move annotationStrategy components to a separate directory, simplify
dbrembilla Mar 20, 2026
6447b85
Remove reference image column, rethink image upload system
dbrembilla Mar 20, 2026
3d2074e
Fix styling
dbrembilla Mar 20, 2026
1535612
Fix role as suggested by review
dbrembilla Mar 20, 2026
3ec391e
Introduce disabled labels in labelTrees
dbrembilla Mar 20, 2026
18b1c4a
Add custom request to store annotation strategies
dbrembilla Mar 20, 2026
4a3c197
Remove reference image from docstring and tests
dbrembilla Mar 20, 2026
aa593dc
Align tests to the new upload rules
dbrembilla Mar 20, 2026
2c68e10
Fix docstring
dbrembilla Mar 20, 2026
1dddc45
Changes error management, fixes bug in displaying images and reload
dbrembilla Mar 20, 2026
5a10011
Merge branch 'master' of https://github.com/biigle/core into 235-anno…
dbrembilla Mar 20, 2026
7b0292f
Fix docstring
dbrembilla Mar 24, 2026
4b13f3c
Change the AnnotationStrategyLabel model's id, fix route of images
dbrembilla Mar 24, 2026
6fb8bda
Fix access to AnnotationStrategy tabs
dbrembilla Mar 24, 2026
4e920e3
Remove now useless methods from api resource
dbrembilla Mar 24, 2026
ad1c532
Remove disabled labels management
dbrembilla Mar 24, 2026
7b0a2a9
Fix strategy and label descriptions
dbrembilla Mar 24, 2026
bd5681f
Adds annotation strategy section to the manual
dbrembilla Mar 24, 2026
8b6cb87
Revamp UI; use Messages to communicate errors
dbrembilla Mar 24, 2026
a3a1a95
Adds annotationStrategy to the other project views
dbrembilla Mar 24, 2026
8c69311
Fix docstring
dbrembilla Mar 24, 2026
f88caa0
Apply PHP fix
dbrembilla Mar 24, 2026
3285afb
Fix update of the annotation strategy
dbrembilla Mar 24, 2026
96c501d
Improves placeholder of typeahead
dbrembilla Mar 24, 2026
9ee4864
Fix case for images that are too small
dbrembilla Apr 9, 2026
b63bd0a
Implements different annotation strategy UX
dbrembilla Apr 9, 2026
e3bff8f
Changes the naming to annotation guideline, introduces new UI
dbrembilla Apr 14, 2026
248896d
Add icon to labels within guideline
dbrembilla Apr 15, 2026
5e2044a
Add delete method to annotationGuideline
dbrembilla Apr 15, 2026
bb6fae0
Fix label in guideline highlighting
dbrembilla Apr 15, 2026
d355805
Change icon for guideline
dbrembilla Apr 15, 2026
930166c
Fix logic errors and syntax
dbrembilla Apr 15, 2026
c66a351
Introduces models for guidelines
dbrembilla Apr 15, 2026
36ef330
Fix tests for AnnotationGuidelineLabel
dbrembilla Apr 15, 2026
da6c8e3
Apply composer fix
dbrembilla Apr 15, 2026
0adf6e4
Merge pull request #1431 from biigle/annotation-strategy-option-2
dbrembilla Apr 15, 2026
c213601
Adds missing annotation guideline blade template without overflow
dbrembilla Apr 16, 2026
2b284a2
Fix overflow annotation guideline
dbrembilla Apr 16, 2026
dce7876
Fix saving of annotationGuidelineLabels when saving guideline
dbrembilla Apr 17, 2026
0809e03
Adapt manual to guideline
dbrembilla Apr 17, 2026
74fab7b
Fix image refreshing, require description before adding a label
dbrembilla Apr 20, 2026
06146d6
Fix bug in refreshCount prop
dbrembilla Apr 20, 2026
d00fa72
Fix syntax and remove unused methods
dbrembilla Apr 20, 2026
1829a80
Remove more useless functions
dbrembilla Apr 21, 2026
10c48ec
Remove useless prop and structure
dbrembilla Apr 21, 2026
0e0a4fd
Implement new guideline icon and label trees filter mechanism
mzur Apr 29, 2026
c082c9e
Fix AnnotationGuideline tests
dbrembilla Apr 30, 2026
35f2101
Fix annotation guideline storage disk; apply suggestion from review
dbrembilla Apr 30, 2026
e441bb1
Transform referenceImage to boolean to allow storage of information o…
dbrembilla Apr 30, 2026
ca32108
Apply review suggestion, fix computed property
dbrembilla Apr 30, 2026
5ee50aa
Remove description requirement for guideline, fix saving, ref image
dbrembilla Apr 30, 2026
d1bb311
Fix filter by guideline if guideline is empty
mzur May 5, 2026
096aaa1
Merge branch '235-annotation-strategy-introduction' into 235-annotati…
mzur May 5, 2026
546a6df
Fix reactivity of annotation guideline labels in label trees
mzur May 5, 2026
b9ddb13
Fix label favourites with annotation guideline filtering
mzur May 5, 2026
2246140
Fix deletion of favourite label storage with filtering
mzur May 5, 2026
cd706c6
Merge pull request #1452 from biigle/235-annotation-strategy-introduc…
mzur May 5, 2026
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
59 changes: 59 additions & 0 deletions app/AnnotationGuideline.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Biigle;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

/**
* This Model describes the annotation guideline of a Project
*
* @property int $id
*/
class AnnotationGuideline extends Model
{
use HasFactory;

/**
* The attributes that should be casted to native types.
*
* @var array<string, string>
*/
protected $casts = [
'id' => 'int',
'project' => 'int',
'description' => 'string',
];

protected $fillable = [
'project',
'description',
];

/**
* Don't maintain timestamps for this model.
*
* @var bool
*/
public $timestamps = false;

/**
* The project this guideline belongs to.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Project, $this>
*/
public function project()
{
return $this->belongsTo(Project::class, 'project');
}

/**
* The labels within this guideline.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany<AnnotationGuidelineLabel, $this>
*/
public function guidelineLabels()
{
return $this->hasMany(AnnotationGuidelineLabel::class, 'annotation_guideline');
}
}
58 changes: 58 additions & 0 deletions app/AnnotationGuidelineLabel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace Biigle;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

/**
* Model for labels within an annotation guideline.
*
* @property int $id
*/
class AnnotationGuidelineLabel extends Model
{
use HasFactory;

/**
* The attributes that should be casted to native types.
*
* @var array<string, string>
*/
protected $casts = [
'annotation_guideline' => 'int',
'label' => 'int',
'shape' => 'int',
'description' => 'string',
'reference_image' => 'boolean',
];

/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'annotation_guideline',
'label',
'shape',
'description',
'reference_image',
];
/**
* Don't maintain timestamps for this model.
*
* @var bool
*/
public $timestamps = false;

/**
* The labels that have a guideline for their annotation
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Label, $this>
*/
public function label()
{
return $this->belongsTo(Label::class, 'label');
}
}
110 changes: 110 additions & 0 deletions app/Http/Controllers/Api/Projects/AnnotationGuidelineController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

namespace Biigle\Http\Controllers\Api\Projects;

use Biigle\AnnotationGuideline;
use Biigle\Http\Controllers\Api\Controller;
use Biigle\Project;
use Illuminate\Http\Request;
use Storage;

class AnnotationGuidelineController extends Controller
{
/**
* Get the annotation guideline for the given project and the associated labels
*
* @api {get} projects/:pid/annotation-guideline Get the annotation guideline for the given project
* @apiGroup Projects
* @apiName AnnotationGuideline
* @apiParam {Number} id The Project ID
* @apiPermission projectAdmin
* @apiDescription Returns the annotation guideline and the associated labels
*
* @apiSuccessExample {json} Success response:
* {"annotation_guideline":[{
* "id":1,
* "project":2,
* "description":"guideline description"
* }],
* "annotation_guideline_labels" : [{
* "annotation_guideline": 1,
* "label":4,
* "shape":7,
* "description":"description of a label",
* "label":
* {
* "id":4,
* "name":"something else",
* },
* }]}
*
*/
public function index($id)
{
$project = Project::findOrFail($id);
$this->authorize('update', $project);
$guideline = AnnotationGuideline::where(['project'=> $id])
->firstOrFail();
$guidelineLabels = $guideline
->guidelineLabels()
->select()
->with('label')
->get();
return ['annotation_guideline' => $guideline, 'annotation_guideline_labels' => $guidelineLabels];

}

/**
* Update the annotation guideline for the given project
*
* @api {post} projects/:pid/annotation-guideline Update the annotation guideline for the given project
* @apiGroup Projects
* @apiName AnnotationGuideline
* @apiParam {Number} id The Project ID
* @apiParam {String} description A description on how to annotate the guideline
* @apiPermission projectAdmin
* @apiDescription Edit the annotation guideline associated with the given ID
*
* @param int $id Project ID
*/
public function update(Request $request, int $id)
{
$request->validate([
'description' => 'nullable|string|min:1',
]);

$project = Project::findOrFail($id);
$this->authorize('update', $project);

AnnotationGuideline::updateOrCreate(
['project' => $project->id],
['description' => $request->description]
);

}

/**
* Delete the annotation guideline for the given project
*
* @api {delete} projects/:pid/annotation-guideline Delete the annotation guideline for the given project
* @apiGroup Projects
* @apiName AnnotationGuideline
* @apiParam {Number} id The Project ID
* @apiPermission projectAdmin
* @apiDescription Delete the annotation guideline associated with the given ID
*/
public function delete(Request $request)
{
$project = Project::findOrFail($request->id);
$this->authorize('update', $project);
$annotationGuideline = AnnotationGuideline::where(['project'=> $project->id])->firstOrFail();
$annotationGuideline->delete();

//Cleanup the directory
$disk = Storage::disk(config('projects.annotation_guideline_storage_disk'));
$url = "$project->id/";
if ($disk->exists($url)) {
$disk->deleteDirectory($url);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace Biigle\Http\Controllers\Api\Projects;

use Biigle\AnnotationGuideline;
use Biigle\AnnotationGuidelineLabel;
use Biigle\Http\Controllers\Api\Controller;
use Biigle\Http\Requests\StoreAnnotationGuidelineLabel;
use Biigle\Label;
use Biigle\Project;
use Biigle\Shape;
use Illuminate\Http\Request;
use Storage;

class AnnotationGuidelineLabelController extends Controller
{
/**
* Update the label within an annotation guideline.
* @api {post} projects/:id/annotation-guideline-label Update the guideline for labels within an annotation guideline
* @apiGroup Projects
* @apiName UpdateAnnotationGuidelineLabel
* @apiPermission projectAdmin
*
* @apiParam {Integer} id THe ID of the project for the annotation guideline for the labels
* @apiParam {Integer} label The ID for the label for the annotation guideline
* @apiParam {Integer} shape (optional) The ID for the preferred shape for the label in the annotation guideline
* @apiParam {String} description (optional) A description of the label
* @apiParam {File} reference_image (optional) The reference image for the label
*
*/
public function update(StoreAnnotationGuidelineLabel $request)
{

$projectId = $request->project->id;
$annotationGuideline = AnnotationGuideline::where(['project' => $projectId])->firstOrFail();
$validated = $request->validated();

$labelId = $validated['label'];
$label = Label::findOrFail($labelId);

$shapeId = $validated['shape'] ?? null;
if (!is_null($shapeId)) {
$shapeId = Shape::findOrFail($shapeId)->id;
}

$description = $validated['description'] ?? null;
$referenceImage = $validated['reference_image'] ?? null;

$disk = Storage::disk(config('projects.annotation_guideline_storage_disk'));

$hasReferenceImage = !is_null($referenceImage);
if ($hasReferenceImage) {
$disk->putFileAs("$projectId", $referenceImage, "$label->id.jpg");
}

//If image already exists, avoid problems
$imageExists = $disk->exists("$projectId/$label->id.jpg");
AnnotationGuidelineLabel::updateOrCreate(
[
'annotation_guideline' => $annotationGuideline->id,
'label' => $label->id,
],
[
'shape' => $shapeId,
'description' => $description,
'reference_image' => $imageExists,
]
);
}

/**
* Delete a label from a guideline.
*
* @api {delete} projects/:id/annotation-guideline-labels/delete-image Delete a reference image
* @apiGroup Projects
* @apiName DeleteReferenceImage
* @apiPermission projectAdmin
*
* @apiParam {Integer} id The ID of the project for the annotation guideline for the labels
*
*/
public function delete(Request $request)
{
$project = Project::findOrFail($request->id);
$label = Label::findOrFail($request->label);
$annotationGuideline = AnnotationGuideline::where(['project' => $project->id])->firstOrFail();
$annotationGuidelineLabel = $annotationGuideline->guidelineLabels()->where(['label' => $label->id])->firstOrFail();

$this->authorize('update', $project);

$annotationGuidelineLabel->delete();

$disk = Storage::disk(config('projects.annotation_guideline_storage_disk'));
$url = "$project->id/$label->id.jpg";
if ($disk->exists($url)) {
$disk->delete($url);
}
}

/**
* Delete a reference image.
*
* @api {delete} projects/:id/annotation-guideline-labels/delete-image Delete a reference image
* @apiGroup Projects
* @apiName DeleteReferenceImage
* @apiPermission projectAdmin
*
* @apiParam {Integer} id The ID of the project for the annotation guideline for the labels
*
*/
public function deleteReferenceImage(Request $request)
{
$project = Project::findOrFail($request->id);
$label = Label::findOrFail($request->label);
$annotationGuideline = AnnotationGuideline::where(['project' => $project->id])->firstOrFail();
$annotationGuidelineLabel = $annotationGuideline->guidelineLabels()->where(['label' => $label->id])->firstOrFail();

$this->authorize('update', $project);

$annotationGuidelineLabel->update(['reference_image' => false]);

$disk = Storage::disk(config('projects.annotation_guideline_storage_disk'));
$url = "$project->id/$label->id.jpg";
if ($disk->exists($url)) {
$disk->delete($url);
}
}
}
Loading