Refactor annotation guideline implementation#1456
Open
mzur wants to merge 27 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Refactors the annotation guideline feature end-to-end (DB schema, backend APIs, authorization, storage cleanup, and UI) to support CRUD for guideline labels and reference images, plus proper cleanup when related entities are deleted.
Changes:
- Reworked annotation guideline persistence to a
annotation_guidelinesmodel +annotation_guideline_labelpivot (UUID-based) and added factories/tests. - Replaced the legacy guideline UI with a new Vue component and updated API resources/routes to a more RESTful structure.
- Added observers and model hooks to delete stored reference images when projects/label trees/labels/guidelines/guideline-labels are deleted.
Reviewed changes
Copilot reviewed 43 out of 43 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/php/ProjectTest.php | Adds relationship + deletion cleanup coverage for project → guideline reference images. |
| tests/php/Policies/AnnotationGuidelinePolicyTest.php | Verifies guideline update authorization rules by role. |
| tests/php/LabelTreeTest.php | Adds cleanup test for label tree deletion removing guideline label reference images. |
| tests/php/LabelTest.php | Adds cleanup test for label deletion removing guideline label reference images. |
| tests/php/Http/Controllers/Views/Projects/AnnotationGuidelineControllerTest.php | Updates view test to new project_id field naming. |
| tests/php/Http/Controllers/Api/Projects/AnnotationGuidelineLabelControllerTest.php | Replaces old monolithic test with focused REST endpoint tests (store/destroy + reference image behaviors). |
| tests/php/Http/Controllers/Api/Projects/AnnotationGuidelineControllerTest.php | Updates API tests for new routes, permissions, and behaviors (store/update/destroy). |
| tests/php/AnnotationGuidelineTest.php | Adds model-level tests for constraints, relationships, and cascade behavior. |
| tests/php/AnnotationGuidelineLabelTest.php | Adds pivot model tests (UUID, uniqueness, cascade behaviors). |
| routes/api.php | Replaces legacy endpoints with resource routes for guidelines and guideline labels. |
| resources/views/projects/show/tabs.blade.php | Shows the Guideline tab based on existence of a guideline via relationship query. |
| resources/views/projects/show/annotation-guideline.blade.php | Simplifies JS bootstrap data to rely on annotationGuideline including labels/pivot data. |
| resources/views/manual/tutorials/projects/about.blade.php | Updates the manual to document the new guideline workflow and moves/renames the anchor. |
| resources/assets/js/projects/components/annotationGuideline.vue | New consolidated guideline UI (label selection, shape/description/image editing, manage dropdown). |
| resources/assets/js/projects/components/annotation_guideline/resizeImage.js | Removes old image resize helper (logic moved into new component). |
| resources/assets/js/projects/components/annotation_guideline/annotationGuidelineLabelImage.vue | Removes old subcomponent (replaced by new consolidated component). |
| resources/assets/js/projects/components/annotation_guideline/annotationGuidelineLabel.vue | Removes old label editor component (replaced by new consolidated component). |
| resources/assets/js/projects/components/annotation_guideline/annotationGuideline.vue | Removes old guideline component (replaced by new consolidated component). |
| resources/assets/js/projects/api/annotationGuidelineLabel.js | Updates JS API resource to new guideline-label endpoints. |
| resources/assets/js/projects/api/annotationGuideline.js | Updates JS API resource to new guideline endpoints (project-scoped create + id-scoped update/destroy). |
| resources/assets/js/projects/annotationGuidelineContainer.vue | Points container to the new component path. |
| database/migrations/2026_03_02_103908_create_annotation_guideline_tables.php | Renames/reshapes tables/columns; adds timestamps; introduces UUID pivot key. |
| database/factories/AnnotationGuidelineLabelFactory.php | Adds factory for the guideline-label pivot with UUID. |
| database/factories/AnnotationGuidelineFactory.php | Adds factory for guidelines. |
| config/projects.php | Introduces config for the annotation guideline storage disk. |
| config/filesystems.php | Renames/moves the public storage path for guideline assets. |
| app/Providers/EventServiceProvider.php | Registers new observers for label and label tree cleanup. |
| app/Project.php | Adds annotationGuideline() relationship. |
| app/Policies/AnnotationGuidelinePolicy.php | Adds authorization for updating guidelines (project admin membership). |
| app/Observers/ProjectObserver.php | Ensures guideline deletion is triggered on project deletion to clean storage. |
| app/Observers/LabelTreeObserver.php | Deletes guideline-label pivots for labels in a tree to clean reference images. |
| app/Observers/LabelObserver.php | Deletes guideline-label pivots for a label to clean reference images. |
| app/Http/Requests/StoreAnnotationGuidelineLabel.php | Updates request authorization/validation for guideline-label store endpoint. |
| app/Http/Controllers/Views/Projects/ProjectUserController.php | Removes manual guideline lookup (tab now checks relationship existence). |
| app/Http/Controllers/Views/Projects/ProjectStatisticsController.php | Removes manual guideline lookup (tab now checks relationship existence). |
| app/Http/Controllers/Views/Projects/ProjectsController.php | Removes manual guideline lookup (tab now checks relationship existence). |
| app/Http/Controllers/Views/Projects/ProjectReportsController.php | Removes manual guideline lookup (tab now checks relationship existence). |
| app/Http/Controllers/Views/Projects/ProjectLabelTreeController.php | Removes manual guideline lookup (tab now checks relationship existence). |
| app/Http/Controllers/Views/Projects/AnnotationGuidelineController.php | Refactors view loading logic to use relationship + read-only 404 behavior for non-admins when absent. |
| app/Http/Controllers/Api/Projects/AnnotationGuidelineLabelController.php | Implements REST store/destroy for guideline labels, reference image storage, UUID file naming. |
| app/Http/Controllers/Api/Projects/AnnotationGuidelineController.php | Implements REST index/store/update/destroy for guidelines with updated authorization and responses. |
| app/AnnotationGuidelineLabel.php | Converts to custom pivot model with UUID PK + storage cleanup hooks + computed URL. |
| app/AnnotationGuideline.php | Updates model fields/relationships and adds storage directory cleanup on delete. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+351
to
+358
| canvas.toBlob((blob) => { | ||
| this.referenceImage = new File([blob], file.name, {type: blob.type}); | ||
| if (this.referenceImagePreview?.startsWith('blob:')) { | ||
| URL.revokeObjectURL(this.referenceImagePreview); | ||
| } | ||
| this.referenceImagePreviewLoaded = false; | ||
| this.referenceImagePreview = URL.createObjectURL(blob); | ||
| }, file.type); |
With afterCommit the images are only deleted if the models were deleted successfully. This way we don't end up with models pointing to deleted files.
Comment on lines
+216
to
+220
| labelDescription: '', | ||
| selectedShape: null, | ||
| referenceImage: null, | ||
| referenceImagePreviewLoaded: false, | ||
| referenceImagePreview: null, |
Comment on lines
+304
to
+307
| this.labelDescription = ''; | ||
| this.selectedShape = null; | ||
| this.referenceImage = null; | ||
| this.referenceImagePreviewLoaded = false; |
Comment on lines
+325
to
+330
| addImage(event) { | ||
| const file = event.target.files[0] || null; | ||
| if (!file) { | ||
| this.referenceImage = null; | ||
| return; | ||
| } |
Comment on lines
+377
to
+388
| let formData = new FormData(); | ||
| formData.append('label_id', this.selectedLabel.id); | ||
| formData.append('description', this.labelDescription || ''); | ||
| if (this.selectedShape) { | ||
| formData.append('shape_id', this.selectedShape); | ||
| } | ||
| if (this.referenceImage !== null) { | ||
| formData.append('reference_image', this.referenceImage); | ||
| } else { | ||
| formData.append('reference_image', ''); | ||
| } | ||
|
|
Comment on lines
+345
to
+352
| canvas.toBlob((blob) => { | ||
| this.referenceImage = new File([blob], file.name, {type: blob.type}); | ||
| if (this.referenceImagePreview?.startsWith('blob:')) { | ||
| URL.revokeObjectURL(this.referenceImagePreview); | ||
| } | ||
| this.referenceImagePreviewLoaded = false; | ||
| this.referenceImagePreview = URL.createObjectURL(blob); | ||
| }, file.type); |
Comment on lines
+377
to
+387
| let formData = new FormData(); | ||
| formData.append('label_id', this.selectedLabel.id); | ||
| formData.append('description', this.labelDescription || ''); | ||
| if (this.selectedShape) { | ||
| formData.append('shape_id', this.selectedShape); | ||
| } | ||
| if (this.referenceImage !== null) { | ||
| formData.append('reference_image', this.referenceImage); | ||
| } else { | ||
| formData.append('reference_image', ''); | ||
| } |
| annotationGuideline: null, | ||
| labelDescription: '', | ||
| selectedShape: null, | ||
| referenceImage: null, |
| resetForm() { | ||
| this.labelDescription = ''; | ||
| this.selectedShape = null; | ||
| this.referenceImage = null; |
Comment on lines
+326
to
+330
| const file = event.target.files[0] || null; | ||
| if (!file) { | ||
| this.referenceImage = null; | ||
| return; | ||
| } |
Comment on lines
+377
to
+387
| let formData = new FormData(); | ||
| formData.append('label_id', this.selectedLabel.id); | ||
| formData.append('description', this.labelDescription || ''); | ||
| if (this.selectedShape) { | ||
| formData.append('shape_id', this.selectedShape); | ||
| } | ||
| if (this.referenceImage !== null) { | ||
| formData.append('reference_image', this.referenceImage); | ||
| } else { | ||
| formData.append('reference_image', ''); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(create a new article about guidelines)has_referenceattribute for guideline label