Conversation
…trieval and COCO format export
…and improve error logging
…eation in WithTargetStage method
…adata retrieval and error handling
…ality from projectStore fix: Update isGroundTruth assignment in workspaceStore to avoid defaulting to false
There was a problem hiding this comment.
Pull Request Overview
This PR implements a comprehensive data export feature that allows users to export annotated datasets in COCO format. The feature enables exporting completed tasks from workflow stages with configurable options for including ground truth annotations and predictions.
- Adds export service with metadata retrieval and COCO format export functionality
- Implements export REST API endpoints with rate limiting and project access control
- Integrates export functionality into the frontend with download capabilities
Reviewed Changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| server/Server/Services/Interfaces/IExportService.cs | Defines export service interface with COCO export and metadata methods |
| server/Server/Services/ExportService.cs | Implements core export logic with COCO format generation and annotation processing |
| server/Server/Models/DTOs/Export/*.cs | DTOs for export requests, responses, and COCO dataset format |
| server/Server/Controllers/ExportController.cs | REST API endpoints for export functionality with error handling |
| server/Server/Extensions/ServiceCollectionExtensions.cs | Registers export service in DI container |
| server/Server.Tests/Services/ExportServiceTests.cs | Unit tests for export service functionality |
| frontend/src/services/project/export/*.ts | Frontend export service with download capabilities |
| frontend/src/views/project/TasksView.vue | Adds export button to completion stage tasks view |
| Various workflow and annotation files | Minor fixes and improvements to existing functionality |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| case AnnotationType.POLYLINE: | ||
| var polyline = ExtractPolyline(annotationData); | ||
| cocoAnnotation.Segmentation = new List<object>(); | ||
| cocoAnnotation.Area = 0; // Area is zero for polyline // TODO: Should polyline have an area? |
There was a problem hiding this comment.
The TODO comment should be resolved or removed. If polylines should have zero area, document why. If they should have calculated area (e.g., stroke width consideration), implement the calculation.
| cocoAnnotation.Area = 0; // Area is zero for polyline // TODO: Should polyline have an area? | |
| // Polylines are open paths and do not enclose any area, so area is set to zero. | |
| cocoAnnotation.Area = 0; |
| case AnnotationType.LINE: | ||
| var line = ExtractLine(annotationData); | ||
| cocoAnnotation.Segmentation = new List<object>(); | ||
| cocoAnnotation.Area = 0; // TODO: Should line have an area? |
There was a problem hiding this comment.
The TODO comment should be resolved or removed. Clarify whether lines should have zero area or if area calculation is needed for consistency with COCO format requirements.
| cocoAnnotation.Area = 0; // TODO: Should line have an area? | |
| cocoAnnotation.Area = 0; // Lines have zero area per COCO format |
| // Limit length to 200 characters | ||
| // TODO: Implement proper filename length handling | ||
| if (sanitized.Length > 200) | ||
| { | ||
| var extension = Path.GetExtension(sanitized); | ||
| var nameWithoutExtension = Path.GetFileNameWithoutExtension(sanitized); | ||
| sanitized = nameWithoutExtension[..(200 - extension.Length)] + extension; |
There was a problem hiding this comment.
The TODO comment indicates incomplete implementation. The current truncation logic may corrupt filenames with multi-byte characters or create invalid extensions. Either implement proper filename length handling or document the current approach as sufficient.
| // Limit length to 200 characters | |
| // TODO: Implement proper filename length handling | |
| if (sanitized.Length > 200) | |
| { | |
| var extension = Path.GetExtension(sanitized); | |
| var nameWithoutExtension = Path.GetFileNameWithoutExtension(sanitized); | |
| sanitized = nameWithoutExtension[..(200 - extension.Length)] + extension; | |
| // Limit length to 200 characters, preserving extension | |
| if (sanitized.Length > 200) | |
| { | |
| var extension = Path.GetExtension(sanitized); | |
| var nameWithoutExtension = Path.GetFileNameWithoutExtension(sanitized); | |
| // Ensure extension is not longer than 200 characters | |
| if (extension.Length >= 200) | |
| { | |
| // Truncate extension if necessary | |
| extension = extension[..200]; | |
| sanitized = extension; | |
| } | |
| else | |
| { | |
| var maxNameLength = 200 - extension.Length; | |
| if (nameWithoutExtension.Length > maxNameLength) | |
| { | |
| nameWithoutExtension = nameWithoutExtension[..maxNameLength]; | |
| } | |
| sanitized = nameWithoutExtension + extension; | |
| } |
| } | ||
|
|
||
| if (context.TargetStage.TargetDataSourceId == null) | ||
| if (context.CurrentStage.TargetDataSourceId == null) |
There was a problem hiding this comment.
This change from context.TargetStage.TargetDataSourceId to context.CurrentStage.TargetDataSourceId appears to be a logic error. The asset transfer should move to the target stage's data source, not stay in the current stage's target data source.
|
|
||
| _logger.LogInformation("Transferring asset {AssetId} from data source {FromDataSource} to {ToDataSource}", | ||
| context.Asset.AssetId, context.Asset.DataSourceId, context.TargetStage.TargetDataSourceId.Value); | ||
| context.Asset.AssetId, context.Asset.DataSourceId, context.CurrentStage.TargetDataSourceId.Value); |
There was a problem hiding this comment.
Consistent with the previous issue, this should be context.TargetStage.TargetDataSourceId.Value to transfer the asset to the target stage's data source, not the current stage's target data source.
…tes and refine annotation filtering
No description provided.