diff --git a/Azaion.Common/DTO/AnnotationThumbnail.cs b/Azaion.Common/DTO/AnnotationThumbnail.cs index 0fd7c8a..988a267 100644 --- a/Azaion.Common/DTO/AnnotationThumbnail.cs +++ b/Azaion.Common/DTO/AnnotationThumbnail.cs @@ -35,4 +35,6 @@ public class AnnotationThumbnail(Annotation annotation) : INotifyPropertyChanged { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + + public void UpdateUI() => OnPropertyChanged(nameof(IsSeed)); } \ No newline at end of file diff --git a/Azaion.Common/Database/Annotation.cs b/Azaion.Common/Database/Annotation.cs index b77e41e..5b5c04c 100644 --- a/Azaion.Common/Database/Annotation.cs +++ b/Azaion.Common/Database/Annotation.cs @@ -31,6 +31,9 @@ public class Annotation [IgnoreMember]public SourceEnum Source { get; set; } [IgnoreMember]public AnnotationStatus AnnotationStatus { get; set; } + [IgnoreMember]public DateTime ValidateDate { get; set; } + [IgnoreMember]public string ValidateEmail { get; set; } = null!; + [Key("d")] public IEnumerable Detections { get; set; } = null!; [Key("t")] public long Milliseconds { get; set; } diff --git a/Azaion.Common/Services/AnnotationService.cs b/Azaion.Common/Services/AnnotationService.cs index ab6fa21..8271588 100644 --- a/Azaion.Common/Services/AnnotationService.cs +++ b/Azaion.Common/Services/AnnotationService.cs @@ -94,7 +94,6 @@ public class AnnotationService : INotificationHandler new MemoryStream(msg.Image), msg.CreatedRole, msg.CreatedEmail, - generateThumbnail: true, fromQueue: true, token: cancellationToken); } @@ -106,17 +105,12 @@ public class AnnotationService : INotificationHandler { a.Time = TimeSpan.FromMilliseconds(a.Milliseconds); return await SaveAnnotationInner(DateTime.Now, a.OriginalMediaName, a.Time, a.Detections.ToList(), - SourceEnum.AI, new MemoryStream(a.Image), _api.CurrentUser.Role, _api.CurrentUser.Email, generateThumbnail: true, token: ct); + SourceEnum.AI, new MemoryStream(a.Image), _api.CurrentUser.Role, _api.CurrentUser.Email, token: ct); } //Manual public async Task SaveAnnotation(string originalMediaName, TimeSpan time, List detections, Stream? stream = null, CancellationToken token = default) => await SaveAnnotationInner(DateTime.UtcNow, originalMediaName, time, detections, SourceEnum.Manual, stream, - _api.CurrentUser.Role, _api.CurrentUser.Email, generateThumbnail: true, token: token); - - //Manual Validate existing - public async Task ValidateAnnotation(Annotation annotation, CancellationToken token = default) => - await SaveAnnotationInner(DateTime.UtcNow, annotation.OriginalMediaName, annotation.Time, annotation.Detections.ToList(), SourceEnum.Manual, null, _api.CurrentUser.Role, _api.CurrentUser.Email, token: token); // Manual save from Validators -> Validated -> stream: azaion-annotations-confirm @@ -124,11 +118,9 @@ public class AnnotationService : INotificationHandler private async Task SaveAnnotationInner(DateTime createdDate, string originalMediaName, TimeSpan time, List detections, SourceEnum source, Stream? stream, RoleEnum userRole, string createdEmail, - bool generateThumbnail = false, bool fromQueue = false, CancellationToken token = default) { - AnnotationStatus status; var fName = originalMediaName.ToTimeName(time); var annotation = await _dbFactory.Run(async db => @@ -179,13 +171,10 @@ public class AnnotationService : INotificationHandler img.Save(annotation.ImagePath, ImageFormat.Jpeg); //todo: check png images coming from queue } await YoloLabel.WriteToFile(detections, annotation.LabelPath, token); - if (generateThumbnail) - { - await _galleryService.CreateThumbnail(annotation, token); - if (_uiConfig.GenerateAnnotatedImage) - await _galleryService.CreateAnnotatedImage(annotation, token); - } + await _galleryService.CreateThumbnail(annotation, token); + if (_uiConfig.GenerateAnnotatedImage) + await _galleryService.CreateAnnotatedImage(annotation, token); if (!fromQueue) //Send to queue only if we're not getting from queue already await _producer.SendToInnerQueue(annotation, token); @@ -195,10 +184,32 @@ public class AnnotationService : INotificationHandler { _dbFactory.SaveToDisk(); await Task.CompletedTask; - }, SaveTaskId, TimeSpan.FromSeconds(5)); + }, SaveTaskId, TimeSpan.FromSeconds(5), true); return annotation; } + public async Task ValidateAnnotations(List annotations, CancellationToken token = default) + { + if (!_api.CurrentUser.Role.IsValidator()) + return; + + var annNames = annotations.Select(x => x.Name).ToHashSet(); + await _dbFactory.Run(async db => + { + await db.Annotations + .Where(x => annNames.Contains(x.Name)) + .Set(x => x.AnnotationStatus, AnnotationStatus.Validated) + .Set(x => x.ValidateDate, DateTime.UtcNow) + .Set(x => x.ValidateEmail, _api.CurrentUser.Email) + .UpdateAsync(token: token); + }); + ThrottleExt.Throttle(async () => + { + _dbFactory.SaveToDisk(); + await Task.CompletedTask; + }, SaveTaskId, TimeSpan.FromSeconds(5), true); + } + public async Task Handle(AnnotationsDeletedEvent notification, CancellationToken cancellationToken) { await _dbFactory.DeleteAnnotations(notification.Annotations, cancellationToken); diff --git a/Azaion.Dataset/DatasetExplorerEventHandler.cs b/Azaion.Dataset/DatasetExplorerEventHandler.cs index 09b41e4..de4b3c8 100644 --- a/Azaion.Dataset/DatasetExplorerEventHandler.cs +++ b/Azaion.Dataset/DatasetExplorerEventHandler.cs @@ -2,6 +2,7 @@ using System.Windows; using System.Windows.Input; using System.Windows.Threading; +using Azaion.Common.Database; using Azaion.Common.DTO; using Azaion.Common.DTO.Queue; using Azaion.Common.Events; @@ -96,8 +97,14 @@ public class DatasetExplorerEventHandler( var annotations = datasetExplorer.ThumbnailsView.SelectedItems.Cast() .Select(x => x.Annotation) .ToList(); - foreach (var annotation in annotations) - await annotationService.ValidateAnnotation(annotation, cancellationToken); + await annotationService.ValidateAnnotations(annotations, cancellationToken); + foreach (var ann in datasetExplorer.SelectedAnnotations.Where(x => annotations.Contains(x.Annotation))) + { + ann.Annotation.AnnotationStatus = AnnotationStatus.Validated; + if (datasetExplorer.SelectedAnnotationDict.TryGetValue(ann.Annotation.Name, out var value)) + value.Annotation.AnnotationStatus = AnnotationStatus.Validated; + ann.UpdateUI(); + } break; } } diff --git a/Azaion.Suite/config.production.json b/Azaion.Suite/config.production.json index d8e778a..5c34cc2 100644 --- a/Azaion.Suite/config.production.json +++ b/Azaion.Suite/config.production.json @@ -24,6 +24,7 @@ }, "UIConfig": { "LeftPanelWidth": 170.0, - "RightPanelWidth": 120.0 + "RightPanelWidth": 120.0, + "GenerateAnnotatedImage": true } } \ No newline at end of file